home *** CD-ROM | disk | FTP | other *** search
/ Amiga Games Extra 1996 September / Amiga Games Extra CD-ROM 9-1996.iso / userbox / publicdomain / vim-4.2 / src / search.c < prev    next >
C/C++ Source or Header  |  1996-06-09  |  72KB  |  3,138 lines

  1. /* vi:set ts=4 sw=4:
  2.  *
  3.  * VIM - Vi IMproved        by Bram Moolenaar
  4.  *
  5.  * Do ":help uganda"  in Vim to read copying and usage conditions.
  6.  * Do ":help credits" in Vim to see a list of people who contributed.
  7.  */
  8. /*
  9.  * search.c: code for normal mode searching commands
  10.  */
  11.  
  12. #include "vim.h"
  13. #include "globals.h"
  14. #include "proto.h"
  15. #include "option.h"
  16. #include "ops.h"        /* for op_inclusive */
  17.  
  18. /* modified Henry Spencer's regular expression routines */
  19. #include "regexp.h"
  20.  
  21. static int inmacro __ARGS((char_u *, char_u *));
  22. static int cls __ARGS((void));
  23. static int skip_chars __ARGS((int, int));
  24. static void back_in_line __ARGS((void));
  25. static void find_first_blank __ARGS((FPOS *));
  26. static void show_pat_in_path __ARGS((char_u *, int,
  27.                                          int, int, FILE *, linenr_t *, long));
  28. static void give_warning __ARGS((char_u *));
  29.  
  30. static char_u *top_bot_msg = (char_u *)"search hit TOP, continuing at BOTTOM";
  31. static char_u *bot_top_msg = (char_u *)"search hit BOTTOM, continuing at TOP";
  32.  
  33. /*
  34.  * This file contains various searching-related routines. These fall into
  35.  * three groups:
  36.  * 1. string searches (for /, ?, n, and N)
  37.  * 2. character searches within a single line (for f, F, t, T, etc)
  38.  * 3. "other" kinds of searches like the '%' command, and 'word' searches.
  39.  */
  40.  
  41. /*
  42.  * String searches
  43.  *
  44.  * The string search functions are divided into two levels:
  45.  * lowest:    searchit(); called by do_search() and edit().
  46.  * Highest: do_search(); changes curwin->w_cursor, called by normal().
  47.  *
  48.  * The last search pattern is remembered for repeating the same search.
  49.  * This pattern is shared between the :g, :s, ? and / commands.
  50.  * This is in myregcomp().
  51.  *
  52.  * The actual string matching is done using a heavily modified version of
  53.  * Henry Spencer's regular expression library.
  54.  */
  55.  
  56. /*
  57.  * Two search patterns are remembered: One for the :substitute command and
  58.  * one for other searches. last_pattern points to the one that was
  59.  * used the last time.
  60.  */
  61. static char_u    *search_pattern = NULL;
  62. static int        search_magic = TRUE;
  63. static int        search_no_scs = FALSE;
  64. static char_u    *subst_pattern = NULL;
  65. static int        subst_magic = TRUE;
  66. static int        subst_no_scs = FALSE;
  67. static char_u    *last_pattern = NULL;
  68. static int        last_magic = TRUE;
  69. static int        last_no_scs = FALSE;
  70. static char_u    *mr_pattern = NULL;        /* pattern used by myregcomp() */
  71.  
  72. static int        want_start;                /* looking for start of line? */
  73.  
  74. /*
  75.  * Type used by find_pattern_in_path() to remember which included files have
  76.  * been searched already.
  77.  */
  78. typedef struct SearchedFile
  79. {
  80.     FILE        *fp;        /* File pointer */
  81.     char_u        *name;        /* Full name of file */
  82.     linenr_t    lnum;        /* Line we were up to in file */
  83.     int            matched;    /* Found a match in this file */
  84. } SearchedFile;
  85.  
  86. /*
  87.  * translate search pattern for vim_regcomp()
  88.  *
  89.  * sub_cmd == RE_SEARCH: save pat in search_pattern (normal search command)
  90.  * sub_cmd == RE_SUBST: save pat in subst_pattern (:substitute command)
  91.  * sub_cmd == RE_BOTH: save pat in both patterns (:global command)
  92.  * which_pat == RE_SEARCH: use previous search pattern if "pat" is NULL
  93.  * which_pat == RE_SUBST: use previous sustitute pattern if "pat" is NULL
  94.  * which_pat == RE_LAST: use last used pattern if "pat" is NULL
  95.  * options & SEARCH_HIS: put search string in history
  96.  * options & SEARCH_KEEP: keep previous search pattern
  97.  * 
  98.  */
  99.     regexp *
  100. myregcomp(pat, sub_cmd, which_pat, options)
  101.     char_u    *pat;
  102.     int        sub_cmd;
  103.     int        which_pat;
  104.     int        options;
  105. {
  106.     rc_did_emsg = FALSE;
  107.     reg_magic = p_magic;
  108.     if (pat == NULL || *pat == NUL)        /* use previous search pattern */
  109.     {
  110.         if (which_pat == RE_SEARCH)
  111.         {
  112.             if (search_pattern == NULL)
  113.             {
  114.                 emsg(e_noprevre);
  115.                 rc_did_emsg = TRUE;
  116.                 return (regexp *) NULL;
  117.             }
  118.             pat = search_pattern;
  119.             reg_magic = search_magic;
  120.             no_smartcase = search_no_scs;
  121.         }
  122.         else if (which_pat == RE_SUBST)
  123.         {
  124.             if (subst_pattern == NULL)
  125.             {
  126.                 emsg(e_nopresub);
  127.                 rc_did_emsg = TRUE;
  128.                 return (regexp *) NULL;
  129.             }
  130.             pat = subst_pattern;
  131.             reg_magic = subst_magic;
  132.             no_smartcase = subst_no_scs;
  133.         }
  134.         else    /* which_pat == RE_LAST */
  135.         {
  136.             if (last_pattern == NULL)
  137.             {
  138.                 emsg(e_noprevre);
  139.                 rc_did_emsg = TRUE;
  140.                 return (regexp *) NULL;
  141.             }
  142.             pat = last_pattern;
  143.             reg_magic = last_magic;
  144.             no_smartcase = last_no_scs;
  145.         }
  146.     }
  147.     else if (options & SEARCH_HIS)
  148.         add_to_history(1, pat);            /* put new pattern in history */
  149.  
  150.     mr_pattern = pat;
  151.  
  152.     /*
  153.      * save the currently used pattern in the appropriate place,
  154.      * unless the pattern should not be remembered
  155.      */
  156.     if (!(options & SEARCH_KEEP))
  157.     {
  158.         /*
  159.          * search or global command
  160.          */
  161.         if (sub_cmd == RE_SEARCH || sub_cmd == RE_BOTH)
  162.         {
  163.             if (search_pattern != pat)
  164.             {
  165.                 vim_free(search_pattern);
  166.                 search_pattern = strsave(pat);
  167.                 last_pattern = search_pattern;
  168.                 search_magic = reg_magic;
  169.                 last_magic = reg_magic;        /* Magic sticks with the r.e. */
  170.                 search_no_scs = no_smartcase;
  171.                 last_no_scs = no_smartcase;
  172.             }
  173.         }
  174.         /*
  175.          * substitute or global command
  176.          */
  177.         if (sub_cmd == RE_SUBST || sub_cmd == RE_BOTH)
  178.         {
  179.             if (subst_pattern != pat)
  180.             {
  181.                 vim_free(subst_pattern);
  182.                 subst_pattern = strsave(pat);
  183.                 last_pattern = subst_pattern;
  184.                 subst_magic = reg_magic;
  185.                 last_magic = reg_magic;        /* Magic sticks with the r.e. */
  186.                 subst_no_scs = no_smartcase;
  187.                 last_no_scs = no_smartcase;
  188.             }
  189.         }
  190.     }
  191.  
  192.     want_start = (*pat == '^');    /* looking for start of line? */
  193.     set_reg_ic(pat);            /* tell the vim_regexec routine how to search */
  194.     return vim_regcomp(pat);
  195. }
  196.  
  197. /*
  198.  * Set reg_ic according to p_ic, p_scs and the search pattern.
  199.  */
  200.     void
  201. set_reg_ic(pat)
  202.     char_u    *pat;
  203. {
  204.     char_u *p;
  205.  
  206.     reg_ic = p_ic;
  207.     if (!no_smartcase && p_scs
  208. #ifdef INSERT_EXPAND
  209.                                 && !(ctrl_x_mode && curbuf->b_p_inf)
  210. #endif
  211.                                                                     )
  212.     {
  213.         /* don't ignore case if pattern has uppercase */
  214.         for (p = pat; *p; )
  215.             if (isupper(*p++))
  216.                 reg_ic = FALSE;
  217.     }
  218.     no_smartcase = FALSE;
  219. }
  220.  
  221. /*
  222.  * lowest level search function.
  223.  * Search for 'count'th occurrence of 'str' in direction 'dir'.
  224.  * Start at position 'pos' and return the found position in 'pos'.
  225.  *
  226.  * if (options & SEARCH_MSG) == 0 don't give any messages
  227.  * if (options & SEARCH_MSG) == SEARCH_NFMSG don't give 'notfound' messages
  228.  * if (options & SEARCH_MSG) == SEARCH_MSG give all messages
  229.  * if (options & SEARCH_HIS) put search pattern in history
  230.  * if (options & SEARCH_END) return position at end of match
  231.  * if (options & SEARCH_START) accept match at pos itself
  232.  * if (options & SEARCH_KEEP) keep previous search pattern
  233.  *
  234.  * Return OK for success, FAIL for failure.
  235.  */
  236.     int
  237. searchit(pos, dir, str, count, options, which_pat)
  238.     FPOS    *pos;
  239.     int        dir;
  240.     char_u    *str;
  241.     long    count;
  242.     int        options;
  243.     int        which_pat;
  244. {
  245.     int                    found;
  246.     linenr_t            lnum;            /* no init to shut up Apollo cc */
  247.     regexp                *prog;
  248.     char_u                *ptr;
  249.     register char_u        *match = NULL, *matchend = NULL;    /* init for GCC */
  250.     int                    loop;
  251.     FPOS                start_pos;
  252.     int                    at_first_line;
  253.     int                    extra_col;
  254.     int                    match_ok;
  255.     char_u                *p;
  256.  
  257.     if ((prog = myregcomp(str, RE_SEARCH, which_pat,
  258.                              (options & (SEARCH_HIS + SEARCH_KEEP)))) == NULL)
  259.     {
  260.         if ((options & SEARCH_MSG) && !rc_did_emsg)
  261.             emsg2((char_u *)"Invalid search string: %s", mr_pattern);
  262.         return FAIL;
  263.     }
  264.  
  265.     if (options & SEARCH_START)
  266.         extra_col = 0;
  267.     else
  268.         extra_col = 1;
  269.  
  270.  
  271. /*
  272.  * find the string
  273.  */
  274.     do                            /* loop for count */
  275.     {
  276.         start_pos = *pos;        /* remember start pos for detecting no match */
  277.         found = 0;                /* default: not found */
  278.  
  279.         /*
  280.          * Start searching in current line, unless searching backwards and
  281.          * we're in column 0 or searching forward and we're past the end of
  282.          * the line
  283.          */
  284.         if (dir == BACKWARD && start_pos.col == 0)
  285.         {
  286.             lnum = pos->lnum - 1;
  287.             at_first_line = FALSE;
  288.         }
  289.         else
  290.         {
  291.             lnum = pos->lnum;
  292.             at_first_line = TRUE;
  293.         }
  294.  
  295.         for (loop = 0; loop <= 1; ++loop)    /* loop twice if 'wrapscan' set */
  296.         {
  297.             for ( ; lnum > 0 && lnum <= curbuf->b_ml.ml_line_count;
  298.                                            lnum += dir, at_first_line = FALSE)
  299.             {
  300.                 ptr = ml_get(lnum);
  301.                                             /* forward search, first line */
  302.                 if (dir == FORWARD && at_first_line && start_pos.col > 0 &&
  303.                                                                    want_start)
  304.                     continue;                /* match not possible */
  305.  
  306.                 /*
  307.                  * Look for a match somewhere in the line.
  308.                  */
  309.                 if (vim_regexec(prog, ptr, TRUE))
  310.                 {
  311.                     match = prog->startp[0];
  312.                     matchend = prog->endp[0];
  313.                     
  314.                     /*
  315.                      * Forward search in the first line: match should be after
  316.                      * the start position. If not, continue at the end of the
  317.                      * match (this is vi compatible).
  318.                      */
  319.                     if (dir == FORWARD && at_first_line)
  320.                     {
  321.                         match_ok = TRUE;
  322.                         /*
  323.                          * When *match == NUL the cursor will be put one back
  324.                          * afterwards, compare with that position, otherwise
  325.                          * "/$" will get stuck on end of line.  Same for
  326.                          * matchend.
  327.                          */
  328.                         while ((options & SEARCH_END) ?
  329.                         ((int)(matchend - ptr) - 1 - (int)(*matchend == NUL) <
  330.                                              (int)start_pos.col + extra_col) :
  331.                                   ((int)(match - ptr) - (int)(*match == NUL) <
  332.                                               (int)start_pos.col + extra_col))
  333.                         {
  334.                             /*
  335.                              * If vi-compatible searching, continue at the end
  336.                              * of the match, otherwise continue one position
  337.                              * forward.
  338.                              */
  339.                             if (vim_strchr(p_cpo, CPO_SEARCH) != NULL)
  340.                             {
  341.                                 p = matchend;
  342.                                 if (match == p && *p != NUL)
  343.                                     ++p;
  344.                             }
  345.                             else
  346.                             {
  347.                                 p = match;
  348.                                 if (*p != NUL)
  349.                                     ++p;
  350.                             }
  351.                             if (*p != NUL && vim_regexec(prog, p, FALSE))
  352.                             {
  353.                                 match = prog->startp[0];
  354.                                 matchend = prog->endp[0];
  355.                             }
  356.                             else
  357.                             {
  358.                                 match_ok = FALSE;
  359.                                 break;
  360.                             }
  361.                         }
  362.                         if (!match_ok)
  363.                             continue;
  364.                     }
  365.                     if (dir == BACKWARD && !want_start)
  366.                     {
  367.                         /*
  368.                          * Now, if there are multiple matches on this line,
  369.                          * we have to get the last one. Or the last one before
  370.                          * the cursor, if we're on that line.
  371.                          * When putting the new cursor at the end, compare
  372.                          * relative to the end of the match.
  373.                          */
  374.                         match_ok = FALSE;
  375.                         for (;;)
  376.                         {
  377.                             if (!at_first_line || ((options & SEARCH_END) ?
  378.                                         ((prog->endp[0] - ptr) - 1 + extra_col
  379.                                                       <= (int)start_pos.col) :
  380.                                           ((prog->startp[0] - ptr) + extra_col
  381.                                                       <= (int)start_pos.col)))
  382.                             {
  383.                                 match_ok = TRUE;
  384.                                 match = prog->startp[0];
  385.                                 matchend = prog->endp[0];
  386.                             }
  387.                             else
  388.                                 break;
  389.                             /*
  390.                              * If vi-compatible searching, continue at the end
  391.                              * of the match, otherwise continue one position
  392.                              * forward.
  393.                              */
  394.                             if (vim_strchr(p_cpo, CPO_SEARCH) != NULL)
  395.                             {
  396.                                 p = matchend;
  397.                                 if (p == match && *p != NUL)
  398.                                     ++p;
  399.                             }
  400.                             else
  401.                             {
  402.                                 p = match;
  403.                                 if (*p != NUL)
  404.                                     ++p;
  405.                             }
  406.                             if (*p == NUL || !vim_regexec(prog, p, (int)FALSE))
  407.                                 break;
  408.                         }
  409.  
  410.                         /*
  411.                          * If there is only a match after the cursor, skip
  412.                          * this match.
  413.                          */
  414.                         if (!match_ok)
  415.                             continue;
  416.                     }
  417.  
  418.                     pos->lnum = lnum;
  419.                     if (options & SEARCH_END && !(options & SEARCH_NOOF))
  420.                         pos->col = (int) (matchend - ptr - 1);
  421.                     else
  422.                         pos->col = (int) (match - ptr);
  423.                     found = 1;
  424.                     break;
  425.                 }
  426.                 line_breakcheck();        /* stop if ctrl-C typed */
  427.                 if (got_int)
  428.                     break;
  429.  
  430.                 if (loop && lnum == start_pos.lnum)
  431.                     break;            /* if second loop, stop where started */
  432.             }
  433.             at_first_line = FALSE;
  434.  
  435.             /*
  436.              * stop the search if wrapscan isn't set, after an interrupt and
  437.              * after a match
  438.              */
  439.             if (!p_ws || got_int || found)
  440.                 break;
  441.  
  442.             /*
  443.              * If 'wrapscan' is set we continue at the other end of the file.
  444.              * If 'shortmess' does not contain 's', we give a message.
  445.              * This message is also remembered in keep_msg for when the screen
  446.              * is redrawn. The keep_msg is cleared whenever another message is
  447.              * written.
  448.              */
  449.             if (dir == BACKWARD)    /* start second loop at the other end */
  450.             {
  451.                 lnum = curbuf->b_ml.ml_line_count;
  452.                 if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG))
  453.                     give_warning(top_bot_msg);
  454.             }
  455.             else
  456.             {
  457.                 lnum = 1;
  458.                 if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG))
  459.                     give_warning(bot_top_msg);
  460.             }
  461.         }
  462.         if (got_int)
  463.             break;
  464.     }
  465.     while (--count > 0 && found);    /* stop after count matches or no match */
  466.  
  467.     vim_free(prog);
  468.  
  469.     if (!found)                /* did not find it */
  470.     {
  471.         if (got_int)
  472.             emsg(e_interr);
  473.         else if ((options & SEARCH_MSG) == SEARCH_MSG)
  474.         {
  475.             if (p_ws)
  476.                 EMSG2("Pattern not found: %s", mr_pattern);
  477.             else if (lnum == 0)
  478.                 EMSG2("search hit TOP without match for: %s", mr_pattern);
  479.             else
  480.                 EMSG2("search hit BOTTOM without match for: %s", mr_pattern);
  481.         }
  482.         return FAIL;
  483.     }
  484.     search_match_len = matchend - match;
  485.  
  486.     return OK;
  487. }
  488.  
  489. /*
  490.  * Highest level string search function.
  491.  * Search for the 'count'th occurence of string 'str' in direction 'dirc'
  492.  *                  If 'dirc' is 0: use previous dir.
  493.  *    If 'str' is NULL or empty : use previous string.
  494.  *      If 'options & SEARCH_REV' : go in reverse of previous dir.
  495.  *      If 'options & SEARCH_ECHO': echo the search command and handle options
  496.  *      If 'options & SEARCH_MSG' : may give error message
  497.  *      If 'options & SEARCH_OPT' : interpret optional flags
  498.  *      If 'options & SEARCH_HIS' : put search pattern in history
  499.  *      If 'options & SEARCH_NOOF': don't add offset to position
  500.  *      If 'options & SEARCH_MARK': set previous context mark
  501.  *      If 'options & SEARCH_KEEP': keep previous search pattern
  502.  *
  503.  * Careful: If lastoffline == TRUE and lastoff == 0 this makes the
  504.  * movement linewise without moving the match position.
  505.  *
  506.  * return 0 for failure, 1 for found, 2 for found and line offset added
  507.  */
  508.     int
  509. do_search(dirc, str, count, options)
  510.     int                dirc;
  511.     char_u           *str;
  512.     long            count;
  513.     int                options;
  514. {
  515.     FPOS            pos;        /* position of the last match */
  516.     char_u            *searchstr;
  517.     static int        lastsdir = '/';    /* previous search direction */
  518.     static int        lastoffline;/* previous/current search has line offset */
  519.     static int        lastend;    /* previous/current search set cursor at end */
  520.     static long        lastoff;    /* previous/current line or char offset */
  521.     int                old_lastsdir;
  522.     int                old_lastoffline;
  523.     int                old_lastend;
  524.     long            old_lastoff;
  525.     int                retval;        /* Return value */
  526.     register char_u    *p;
  527.     register long    c;
  528.     char_u            *dircp;
  529.     int                i = 0;        /* init for GCC */
  530.  
  531.     /*
  532.      * A line offset is not remembered, this is vi compatible.
  533.      */
  534.     if (lastoffline && vim_strchr(p_cpo, CPO_LINEOFF) != NULL)
  535.     {
  536.         lastoffline = FALSE;
  537.         lastoff = 0;
  538.     }
  539.  
  540.     /*
  541.      * Save the values for when (options & SEARCH_KEEP) is used.
  542.      * (there is no "if ()" around this because gcc wants them initialized)
  543.      */
  544.     old_lastsdir = lastsdir;
  545.     old_lastoffline = lastoffline;
  546.     old_lastend = lastend;
  547.     old_lastoff = lastoff;
  548.  
  549.     pos = curwin->w_cursor;        /* start searching at the cursor position */
  550.  
  551.     /*
  552.      * Find out the direction of the search.
  553.      */
  554.     if (dirc == 0)
  555.         dirc = lastsdir;
  556.     else
  557.         lastsdir = dirc;
  558.     if (options & SEARCH_REV)
  559.     {
  560. #ifdef WIN32
  561.         /* There is a bug in the Visual C++ 2.2 compiler which means that
  562.          * dirc always ends up being '/' */
  563.         dirc = (dirc == '/')  ?  '?'  :  '/';
  564. #else
  565.         if (dirc == '/')
  566.             dirc = '?';
  567.         else
  568.             dirc = '/';
  569. #endif
  570.     }
  571.  
  572.     /*
  573.      * Repeat the search when pattern followed by ';', e.g. "/foo/;?bar".
  574.      */
  575.     for (;;)
  576.     {
  577.         searchstr = str;
  578.         dircp = NULL;
  579.                                             /* use previous pattern */
  580.         if (str == NULL || *str == NUL || *str == dirc)
  581.         {
  582.             if (search_pattern == NULL)        /* no previous pattern */
  583.             {
  584.                 emsg(e_noprevre);
  585.                 retval = 0;
  586.                 goto end_do_search;
  587.             }
  588.             searchstr = (char_u *)"";  /* make myregcomp() use search_pattern */
  589.         }
  590.  
  591.         if (str != NULL && *str != NUL)    /* look for (new) offset */
  592.         {
  593.             /*
  594.              * Find end of regular expression.
  595.              * If there is a matching '/' or '?', toss it.
  596.              */
  597.             p = skip_regexp(str, dirc);
  598.             if (*p == dirc)
  599.             {
  600.                 dircp = p;        /* remember where we put the NUL */
  601.                 *p++ = NUL;
  602.             }
  603.             lastoffline = FALSE;
  604.             lastend = FALSE;
  605.             lastoff = 0;
  606.             /*
  607.              * Check for a line offset or a character offset.
  608.              * For get_address (echo off) we don't check for a character
  609.              * offset, because it is meaningless and the 's' could be a
  610.              * substitute command.
  611.              */
  612.             if (*p == '+' || *p == '-' || isdigit(*p))
  613.                 lastoffline = TRUE;
  614.             else if ((options & SEARCH_OPT) &&
  615.                                         (*p == 'e' || *p == 's' || *p == 'b'))
  616.             {
  617.                 if (*p == 'e')            /* end */
  618.                     lastend = SEARCH_END;
  619.                 ++p;
  620.             }
  621.             if (isdigit(*p) || *p == '+' || *p == '-')       /* got an offset */
  622.             {
  623.                 if (isdigit(*p) || isdigit(*(p + 1)))
  624.                     lastoff = atol((char *)p);        /* 'nr' or '+nr' or '-nr' */
  625.                 else if (*p == '-')            /* single '-' */
  626.                     lastoff = -1;
  627.                 else                        /* single '+' */
  628.                     lastoff = 1;
  629.                 ++p;
  630.                 while (isdigit(*p))            /* skip number */
  631.                     ++p;
  632.             }
  633.             searchcmdlen = p - str;            /* compute length of search command
  634.                                                             for get_address() */
  635.             str = p;                        /* put str after search command */
  636.         }
  637.  
  638.         if (options & SEARCH_ECHO)
  639.         {
  640.             msg_start();
  641.             msg_outchar(dirc);
  642.             msg_outtrans(*searchstr == NUL ? search_pattern : searchstr);
  643.             if (lastoffline || lastend || lastoff)
  644.             {
  645.                 msg_outchar(dirc);
  646.                 if (lastend)
  647.                     msg_outchar('e');
  648.                 else if (!lastoffline)
  649.                     msg_outchar('s');
  650.                 if (lastoff < 0)
  651.                 {
  652.                     msg_outchar('-');
  653.                     msg_outnum((long)-lastoff);
  654.                 }
  655.                 else if (lastoff > 0 || lastoffline)
  656.                 {
  657.                     msg_outchar('+');
  658.                     msg_outnum((long)lastoff);
  659.                 }
  660.             }
  661.             msg_clr_eos();
  662.             (void)msg_check();
  663.  
  664.             gotocmdline(FALSE);
  665.             flushbuf();
  666.         }
  667.  
  668.         /*
  669.          * If there is a character offset, subtract it from the current
  670.          * position, so we don't get stuck at "?pat?e+2" or "/pat/s-2".
  671.          * This is not done for a line offset, because then we would not be vi
  672.          * compatible.
  673.          */
  674.         if (!lastoffline && lastoff)
  675.         {
  676.             if (lastoff > 0)
  677.             {
  678.                 c = lastoff;
  679.                 while (c--)
  680.                     if ((i = dec(&pos)) != 0)
  681.                         break;
  682.                 if (i == -1)                /* at start of buffer */
  683.                     goto_endofbuf(&pos);
  684.             }
  685.             else
  686.             {
  687.                 c = -lastoff;
  688.                 while (c--)
  689.                     if ((i = inc(&pos)) != 0)
  690.                         break;
  691.                 if (i == -1)                /* at end of buffer */
  692.                 {
  693.                     pos.lnum = 1;
  694.                     pos.col = 0;
  695.                 }
  696.             }
  697.         }
  698.  
  699.         c = searchit(&pos, dirc == '/' ? FORWARD : BACKWARD, searchstr, count,
  700.                 lastend + (options &
  701.                        (SEARCH_KEEP + SEARCH_HIS + SEARCH_MSG +
  702.                            ((str != NULL && *str == ';') ? 0 : SEARCH_NOOF))),
  703.                 2);
  704.         if (dircp != NULL)
  705.             *dircp = dirc;        /* put second '/' or '?' back for normal() */
  706.         if (c == FAIL)
  707.         {
  708.             retval = 0;
  709.             goto end_do_search;
  710.         }
  711.         if (lastend)
  712.             op_inclusive = TRUE;    /* 'e' includes last character */
  713.  
  714.         retval = 1;                    /* pattern found */
  715.  
  716.         /*
  717.          * Add character and/or line offset
  718.          */
  719.         if (!(options & SEARCH_NOOF) || *str == ';')
  720.         {
  721.             if (lastoffline)            /* Add the offset to the line number. */
  722.             {
  723.                 c = pos.lnum + lastoff;
  724.                 if (c < 1)
  725.                     pos.lnum = 1;
  726.                 else if (c > curbuf->b_ml.ml_line_count)
  727.                     pos.lnum = curbuf->b_ml.ml_line_count;
  728.                 else
  729.                     pos.lnum = c;
  730.                 pos.col = 0;
  731.  
  732.                 retval = 2;                /* pattern found, line offset added */
  733.             }
  734.             else
  735.             {
  736.                 if (lastoff > 0)    /* to the right, check for end of line */
  737.                 {
  738.                     p = ml_get_pos(&pos) + 1;
  739.                     c = lastoff;
  740.                     while (c-- && *p++ != NUL)
  741.                         ++pos.col;
  742.                 }
  743.                 else                /* to the left, check for start of line */
  744.                 {
  745.                     if ((c = pos.col + lastoff) < 0)
  746.                         c = 0;
  747.                     pos.col = c;
  748.                 }
  749.             }
  750.         }
  751.  
  752.         /*
  753.          * The search command can be followed by a ';' to do another search.
  754.          * For example: "/pat/;/foo/+3;?bar"
  755.          * This is like doing another search command, except:
  756.          * - The remembered direction '/' or '?' is from the first search.
  757.          * - When an error happens the cursor isn't moved at all.
  758.          * Don't do this when called by get_address() (it handles ';' itself).
  759.          */
  760.         if (!(options & SEARCH_OPT) || str == NULL || *str != ';')
  761.             break;
  762.  
  763.         dirc = *++str;
  764.         if (dirc != '?' && dirc != '/')
  765.         {
  766.             retval = 0;
  767.             EMSG("Expected '?' or '/'  after ';'");
  768.             goto end_do_search;
  769.         }
  770.         ++str;
  771.     }
  772.  
  773.     if (options & SEARCH_MARK)
  774.         setpcmark();
  775.     curwin->w_cursor = pos;
  776.     curwin->w_set_curswant = TRUE;
  777.  
  778. end_do_search:
  779.     if (options & SEARCH_KEEP)
  780.     {
  781.         lastsdir = old_lastsdir;
  782.         lastoffline = old_lastoffline;
  783.         lastend = old_lastend;
  784.         lastoff = old_lastoff;
  785.     }
  786.     return retval;
  787. }
  788.  
  789. /*
  790.  * search_for_exact_line(pos, dir, pat)
  791.  *
  792.  * Search for a line starting with the given pattern (ignoring leading
  793.  * white-space), starting from pos and going in direction dir.    pos will
  794.  * contain the position of the match found.    Blank lines will never match.
  795.  * Return OK for success, or FAIL if no line found.
  796.  */
  797.     int
  798. search_for_exact_line(pos, dir, pat)
  799.     FPOS        *pos;
  800.     int            dir;
  801.     char_u        *pat;
  802. {
  803.     linenr_t    start = 0;
  804.     char_u        *ptr;
  805.     char_u        *p;
  806.  
  807.     if (curbuf->b_ml.ml_line_count == 0)
  808.         return FAIL;
  809.     for (;;)
  810.     {
  811.         pos->lnum += dir;
  812.         if (pos->lnum < 1)
  813.         {
  814.             if (p_ws)
  815.             {
  816.                 pos->lnum = curbuf->b_ml.ml_line_count;
  817.                 if (!shortmess(SHM_SEARCH))
  818.                     give_warning(top_bot_msg);
  819.             }
  820.             else
  821.             {
  822.                 pos->lnum = 1;
  823.                 break;
  824.             }
  825.         }
  826.         else if (pos->lnum > curbuf->b_ml.ml_line_count)
  827.         {
  828.             if (p_ws)
  829.             {
  830.                 pos->lnum = 1;
  831.                 if (!shortmess(SHM_SEARCH))
  832.                     give_warning(bot_top_msg);
  833.             }
  834.             else
  835.             {
  836.                 pos->lnum = 1;
  837.                 break;
  838.             }
  839.         }
  840.         if (pos->lnum == start)
  841.             break;
  842.         if (start == 0)
  843.             start = pos->lnum;
  844.         ptr = ml_get(pos->lnum);
  845.         p = skipwhite(ptr);
  846.         pos->col = p - ptr;
  847.         if (*p != NUL && STRNCMP(p, pat, STRLEN(pat)) == 0)
  848.             return OK;
  849.         else if (*p != NUL && p_ic)
  850.         {
  851.             ptr = pat;
  852.             while (*p && TO_LOWER(*p) == TO_LOWER(*ptr))
  853.             {
  854.                 ++p;
  855.                 ++ptr;
  856.             }
  857.             if (*ptr == NUL)
  858.                 return OK;
  859.         }
  860.     }
  861.     return FAIL;
  862. }
  863.  
  864. /*
  865.  * Character Searches
  866.  */
  867.  
  868. /*
  869.  * searchc(c, dir, type, count)
  870.  *
  871.  * Search for character 'c', in direction 'dir'. If 'type' is 0, move to the
  872.  * position of the character, otherwise move to just before the char.
  873.  * Repeat this 'count' times.
  874.  */
  875.     int
  876. searchc(c, dir, type, count)
  877.     int                c;
  878.     register int    dir;
  879.     int                type;
  880.     long            count;
  881. {
  882.     static int        lastc = NUL;    /* last character searched for */
  883.     static int        lastcdir;        /* last direction of character search */
  884.     static int        lastctype;        /* last type of search ("find" or "to") */
  885.     register int    col;
  886.     char_u            *p;
  887.     int                len;
  888.  
  889.     if (c != NUL)        /* normal search: remember args for repeat */
  890.     {
  891.         if (!KeyStuffed)    /* don't remember when redoing */
  892.         {
  893.             lastc = c;
  894.             lastcdir = dir;
  895.             lastctype = type;
  896.         }
  897.     }
  898.     else                /* repeat previous search */
  899.     {
  900.         if (lastc == NUL)
  901.             return FALSE;
  902.         if (dir)        /* repeat in opposite direction */
  903.             dir = -lastcdir;
  904.         else
  905.             dir = lastcdir;
  906.         type = lastctype;
  907.         c = lastc;
  908.     }
  909.  
  910.     p = ml_get_curline();
  911.     col = curwin->w_cursor.col;
  912.     len = STRLEN(p);
  913.  
  914.     while (count--)
  915.     {
  916.         for (;;)
  917.         {
  918.             if ((col += dir) < 0 || col >= len)
  919.                 return FALSE;
  920.             if (p[col] == c)
  921.                 break;
  922.         }
  923.     }
  924.     if (type)
  925.         col -= dir;
  926.     curwin->w_cursor.col = col;
  927.     return TRUE;
  928. }
  929.  
  930. /*
  931.  * "Other" Searches
  932.  */
  933.  
  934. /*
  935.  * findmatch - find the matching paren or brace
  936.  *
  937.  * Improvement over vi: Braces inside quotes are ignored.
  938.  */
  939.     FPOS *
  940. findmatch(initc)
  941.     int        initc;
  942. {
  943.     return findmatchlimit(initc, 0, 0);
  944. }
  945.  
  946. /*
  947.  * findmatchlimit -- find the matching paren or brace, if it exists within
  948.  * maxtravel lines of here.  A maxtravel of 0 means search until you fall off
  949.  * the edge of the file.
  950.  *
  951.  * flags: FM_BACKWARD    search backwards (when initc is '/', '*' or '#')
  952.  *           FM_FORWARD    search forwards (when initc is '/', '*' or '#')
  953.  *          FM_BLOCKSTOP    stop at start/end of block ({ or } in column 0)
  954.  *          FM_SKIPCOMM    skip comments (not implemented yet!)
  955.  */
  956.  
  957.     FPOS *
  958. findmatchlimit(initc, flags, maxtravel)
  959.     int        initc;
  960.     int        flags;
  961.     int        maxtravel;
  962. {
  963.     static FPOS        pos;                /* current search position */
  964.     int                findc;                /* matching brace */
  965.     int                c;
  966.     int                count = 0;            /* cumulative number of braces */
  967.     int                idx = 0;            /* init for gcc */
  968.     static char_u    table[6] = {'(', ')', '[', ']', '{', '}'};
  969. #ifdef RIGHTLEFT
  970.     static char_u   table_rl[6] = {')', '(', ']', '[', '}', '{'};
  971. #endif
  972.     int                inquote = FALSE;    /* TRUE when inside quotes */
  973.     register char_u    *linep;                /* pointer to current line */
  974.     char_u            *ptr;
  975.     int                do_quotes;            /* check for quotes in current line */
  976.     int                at_start;            /* do_quotes value at start position */
  977.     int                hash_dir = 0;        /* Direction searched for # things */
  978.     int                comment_dir = 0;    /* Direction searched for comments */
  979.     FPOS            match_pos;            /* Where last slash-star was found */
  980.     int                start_in_quotes;    /* start position is in quotes */
  981.     int                traveled = 0;        /* how far we've searched so far */
  982.     int                ignore_cend = FALSE;    /* ignore comment end */
  983.     int                cpo_match;            /* vi compatible matching */
  984.     int                dir;                /* Direction to search */
  985.  
  986.     pos = curwin->w_cursor;
  987.     linep = ml_get(pos.lnum); 
  988.  
  989.     cpo_match = (vim_strchr(p_cpo, CPO_MATCH) != NULL);
  990.  
  991.     /* Direction to search when initc is '/', '*' or '#' */
  992.     if (flags & FM_BACKWARD)
  993.         dir = BACKWARD;
  994.     else if (flags & FM_FORWARD)
  995.         dir = FORWARD;
  996.     else
  997.         dir = 0;
  998.  
  999.     /*
  1000.      * if initc given, look in the table for the matching character
  1001.      * '/' and '*' are special cases: look for start or end of comment.
  1002.      * When '/' is used, we ignore running backwards into an star-slash, for
  1003.      * "[*" command, we just want to find any comment.
  1004.      */
  1005.     if (initc == '/' || initc == '*')
  1006.     {
  1007.         comment_dir = dir;
  1008.         if (initc == '/')
  1009.             ignore_cend = TRUE;
  1010.         idx = (dir == FORWARD) ? 0 : 1;
  1011.         initc = NUL;
  1012.     }
  1013.     else if (initc != '#' && initc != NUL)
  1014.     {
  1015.         for (idx = 0; idx < 6; ++idx)
  1016. #ifdef RIGHTLEFT
  1017.             if ((curwin->w_p_rl ? table_rl : table)[idx] == initc)
  1018.             {
  1019.                 initc = (curwin->w_p_rl ? table_rl : table)[idx = idx ^ 1];
  1020.                 
  1021. #else
  1022.             if (table[idx] == initc)
  1023.             {
  1024.                 initc = table[idx = idx ^ 1];
  1025. #endif
  1026.                 break;
  1027.             }
  1028.         if (idx == 6)            /* invalid initc! */
  1029.             return NULL;
  1030.     }
  1031.     /*
  1032.      * Either initc is '#', or no initc was given and we need to look under the
  1033.      * cursor.
  1034.      */
  1035.     else
  1036.     {
  1037.         if (initc == '#')
  1038.         {
  1039.             hash_dir = dir;
  1040.         }
  1041.         else
  1042.         {
  1043.             /*
  1044.              * initc was not given, must look for something to match under
  1045.              * or near the cursor.
  1046.              */
  1047.             if (linep[0] == '#' && pos.col == 0)
  1048.             {
  1049.                 /* If it's not #if, #else etc, we should look for a brace
  1050.                  * instead */
  1051.                 for (c = 1; vim_iswhite(linep[c]); c++)
  1052.                     ;
  1053.                 if (STRNCMP(linep + c, "if", (size_t)2) == 0 ||
  1054.                     STRNCMP(linep + c, "endif", (size_t)5) == 0 ||
  1055.                     STRNCMP(linep + c, "el", (size_t)2) == 0)
  1056.                         hash_dir = 1;
  1057.             }
  1058.  
  1059.             /*
  1060.              * Are we on a comment?
  1061.              */
  1062.             else if (linep[pos.col] == '/')
  1063.             {
  1064.                 if (linep[pos.col + 1] == '*')
  1065.                 {
  1066.                     comment_dir = 1;
  1067.                     idx = 0;
  1068.                     pos.col++;
  1069.                 }
  1070.                 else if (pos.col > 0 && linep[pos.col - 1] == '*')
  1071.                 {
  1072.                     comment_dir = -1;
  1073.                     idx = 1;
  1074.                     pos.col--;
  1075.                 }
  1076.             }
  1077.             else if (linep[pos.col] == '*')
  1078.             {
  1079.                 if (linep[pos.col + 1] == '/')
  1080.                 {
  1081.                     comment_dir = -1;
  1082.                     idx = 1;
  1083.                 }
  1084.                 else if (pos.col > 0 && linep[pos.col - 1] == '/')
  1085.                 {
  1086.                     comment_dir = 1;
  1087.                     idx = 0;
  1088.                 }
  1089.             }
  1090.  
  1091.             /*
  1092.              * If we are not on a comment or the # at the start of a line, then
  1093.              * look for brace anywhere on this line after the cursor.
  1094.              */
  1095.             if (!hash_dir && !comment_dir)
  1096.             {
  1097.                 /*
  1098.                  * find the brace under or after the cursor
  1099.                  */
  1100.                 linep = ml_get(pos.lnum); 
  1101.                 idx = 6;                    /* error if this line is empty */
  1102.                 for (;;)
  1103.                 {
  1104.                     initc = linep[pos.col];
  1105.                     if (initc == NUL)
  1106.                         break;
  1107.  
  1108.                     for (idx = 0; idx < 6; ++idx)
  1109. #ifdef RIGHTLEFT
  1110.                         if ((curwin->w_p_rl ? table_rl : table)[idx] == initc)
  1111. #else
  1112.                          if (table[idx] == initc)
  1113. #endif
  1114.                             break;
  1115.                     if (idx != 6)
  1116.                         break;
  1117.                     ++pos.col;
  1118.                 }
  1119.                 if (idx == 6)
  1120.                 {
  1121.                     if (linep[0] == '#')
  1122.                         hash_dir = 1;
  1123.                     else
  1124.                         return NULL;
  1125.                 }
  1126.             }
  1127.         }
  1128.         if (hash_dir)
  1129.         {
  1130.             /*
  1131.              * Look for matching #if, #else, #elif, or #endif
  1132.              */
  1133.             op_motion_type = MLINE;        /* Linewise for this case only */
  1134.             if (initc != '#')
  1135.             {
  1136.                 ptr = skipwhite(linep + 1);
  1137.                 if (STRNCMP(ptr, "if", (size_t)2) == 0 ||
  1138.                                        STRNCMP(ptr, "el", (size_t)2) == 0)
  1139.                     hash_dir = 1;
  1140.                 else if (STRNCMP(ptr, "endif", (size_t)5) == 0)
  1141.                     hash_dir = -1;
  1142.                 else
  1143.                     return NULL;
  1144.             }
  1145.             pos.col = 0;
  1146.             while (!got_int)
  1147.             {
  1148.                 if (hash_dir > 0)
  1149.                 {
  1150.                     if (pos.lnum == curbuf->b_ml.ml_line_count)
  1151.                         break;
  1152.                 }
  1153.                 else if (pos.lnum == 1)
  1154.                     break;
  1155.                 pos.lnum += hash_dir;
  1156.                 linep = ml_get(pos.lnum);
  1157.                 line_breakcheck();
  1158.                 if (linep[0] != '#')
  1159.                     continue;
  1160.                 ptr = linep + 1;
  1161.                 while (*ptr == ' ' || *ptr == TAB)
  1162.                     ptr++;
  1163.                 if (hash_dir > 0)
  1164.                 {
  1165.                     if (STRNCMP(ptr, "if", (size_t)2) == 0)
  1166.                         count++;
  1167.                     else if (STRNCMP(ptr, "el", (size_t)2) == 0)
  1168.                     {
  1169.                         if (count == 0)
  1170.                             return &pos;
  1171.                     }
  1172.                     else if (STRNCMP(ptr, "endif", (size_t)5) == 0)
  1173.                     {
  1174.                         if (count == 0)
  1175.                             return &pos;
  1176.                         count--;
  1177.                     }
  1178.                 }
  1179.                 else
  1180.                 {
  1181.                     if (STRNCMP(ptr, "if", (size_t)2) == 0)
  1182.                     {
  1183.                         if (count == 0)
  1184.                             return &pos;
  1185.                         count--;
  1186.                     }
  1187.                     else if (initc == '#' && STRNCMP(ptr, "el", (size_t)2) == 0)
  1188.                     {
  1189.                         if (count == 0)
  1190.                             return &pos;
  1191.                     }
  1192.                     else if (STRNCMP(ptr, "endif", (size_t)5) == 0)
  1193.                         count++;
  1194.                 }
  1195.             }
  1196.             return NULL;
  1197.         }
  1198.     }
  1199.  
  1200. #ifdef RIGHTLEFT
  1201.     findc = (curwin->w_p_rl ? table_rl : table)[idx ^ 1];
  1202. #else
  1203.      findc = table[idx ^ 1];        /* get matching brace */
  1204. #endif
  1205.     idx &= 1;
  1206.  
  1207.     do_quotes = -1;
  1208.     start_in_quotes = MAYBE;
  1209.     while (!got_int)
  1210.     {
  1211.         /*
  1212.          * Go to the next position, forward or backward. We could use
  1213.          * inc() and dec() here, but that is much slower
  1214.          */
  1215.         if (idx)                        /* backward search */
  1216.         {
  1217.             if (pos.col == 0)            /* at start of line, go to prev. one */
  1218.             {
  1219.                 if (pos.lnum == 1)        /* start of file */
  1220.                     break;
  1221.                 --pos.lnum;
  1222.  
  1223.                 if (maxtravel && traveled++ > maxtravel)
  1224.                     break;
  1225.  
  1226.                 linep = ml_get(pos.lnum);
  1227.                 pos.col = STRLEN(linep);    /* put pos.col on trailing NUL */
  1228.                 do_quotes = -1;
  1229.                 line_breakcheck();
  1230.             }
  1231.             else
  1232.                 --pos.col;
  1233.         }
  1234.         else                            /* forward search */
  1235.         {
  1236.             if (linep[pos.col] == NUL)    /* at end of line, go to next one */
  1237.             {
  1238.                 if (pos.lnum == curbuf->b_ml.ml_line_count) /* end of file */
  1239.                     break;
  1240.                 ++pos.lnum;
  1241.  
  1242.                 if (maxtravel && traveled++ > maxtravel)
  1243.                     break;
  1244.  
  1245.                 linep = ml_get(pos.lnum);
  1246.                 pos.col = 0;
  1247.                 do_quotes = -1;
  1248.                 line_breakcheck();
  1249.             }
  1250.             else
  1251.                 ++pos.col;
  1252.         }
  1253.  
  1254.         /*
  1255.          * If FM_BLOCKSTOP given, stop at a '{' or '}' in column 0.
  1256.          */
  1257.         if (pos.col == 0 && (flags & FM_BLOCKSTOP) &&
  1258.                                          (linep[0] == '{' || linep[0] == '}'))
  1259.         {
  1260.             if (linep[0] == findc && count == 0)        /* match! */
  1261.                 return &pos;
  1262.             break;                                        /* out of scope */
  1263.         }
  1264.  
  1265.         if (comment_dir)
  1266.         {
  1267.             /* Note: comments do not nest, and we ignore quotes in them */
  1268.             if (comment_dir == 1)
  1269.             {
  1270.                 if (linep[pos.col] == '*' && linep[pos.col + 1] == '/')
  1271.                 {
  1272.                     pos.col++;
  1273.                     return &pos;
  1274.                 }
  1275.             }
  1276.             else    /* Searching backwards */
  1277.             {
  1278.                 /*
  1279.                  * A comment may contain slash-star, it may also start or end
  1280.                  * with slash-star-slash.  I'm not using real examples though
  1281.                  * because "gcc -Wall" would complain -- webb
  1282.                  */
  1283.                 if (pos.col == 0)
  1284.                     continue;
  1285.                 else if (linep[pos.col - 1] == '/' && linep[pos.col] == '*')
  1286.                 {
  1287.                     count++;
  1288.                     match_pos = pos;
  1289.                     match_pos.col--;
  1290.                 }
  1291.                 else if (linep[pos.col - 1] == '*' && linep[pos.col] == '/')
  1292.                 {
  1293.                     if (count > 0)
  1294.                         pos = match_pos;
  1295.                     else if (pos.col > 1 && linep[pos.col - 2] == '/')
  1296.                         pos.col -= 2;
  1297.                     else if (ignore_cend)
  1298.                         continue;
  1299.                     else
  1300.                         return NULL;
  1301.                     return &pos;
  1302.                 }
  1303.             }
  1304.             continue;
  1305.         }
  1306.  
  1307.         /*
  1308.          * If smart matching ('cpoptions' does not contain '%'), braces inside
  1309.          * of quotes are ignored, but only if there is an even number of
  1310.          * quotes in the line.
  1311.          */
  1312.         if (cpo_match)
  1313.             do_quotes = 0;
  1314.         else if (do_quotes == -1)
  1315.         {
  1316.             /*
  1317.              * count the number of quotes in the line, skipping \" and '"'
  1318.              */
  1319.             at_start = do_quotes;
  1320.             for (ptr = linep; *ptr; ++ptr)
  1321.             {
  1322.                 if (ptr == linep + curwin->w_cursor.col)
  1323.                     at_start = (do_quotes & 1);
  1324.                 if (*ptr == '"' && (ptr == linep || ptr[-1] != '\\') &&
  1325.                             (ptr == linep || ptr[-1] != '\'' || ptr[1] != '\''))
  1326.                     ++do_quotes;
  1327.             }
  1328.             do_quotes &= 1;            /* result is 1 with even number of quotes */
  1329.  
  1330.             /*
  1331.              * If we find an uneven count, check current line and previous
  1332.              * one for a '\' at the end.
  1333.              */
  1334.             if (!do_quotes)
  1335.             {
  1336.                 inquote = FALSE;
  1337.                 if (ptr[-1] == '\\')
  1338.                 {
  1339.                     do_quotes = 1;
  1340.                     if (start_in_quotes == MAYBE)
  1341.                     {
  1342.                         inquote = !at_start;
  1343.                         if (inquote)
  1344.                             start_in_quotes = TRUE;
  1345.                     }
  1346.                     else if (idx)                /* backward search */
  1347.                         inquote = TRUE;
  1348.                 }
  1349.                 if (pos.lnum > 1)
  1350.                 {
  1351.                     ptr = ml_get(pos.lnum - 1);
  1352.                     if (*ptr && *(ptr + STRLEN(ptr) - 1) == '\\')
  1353.                     {
  1354.                         do_quotes = 1;
  1355.                         if (start_in_quotes == MAYBE)
  1356.                         {
  1357.                             inquote = at_start;
  1358.                             if (inquote)
  1359.                                 start_in_quotes = TRUE;
  1360.                         }
  1361.                         else if (!idx)                /* forward search */
  1362.                             inquote = TRUE;
  1363.                     }
  1364.                 }
  1365.             }
  1366.         }
  1367.         if (start_in_quotes == MAYBE)
  1368.             start_in_quotes = FALSE;
  1369.  
  1370.         /*
  1371.          * If 'smartmatch' is set:
  1372.          *     Things inside quotes are ignored by setting 'inquote'.  If we
  1373.          *     find a quote without a preceding '\' invert 'inquote'.  At the
  1374.          *     end of a line not ending in '\' we reset 'inquote'.
  1375.          *
  1376.          *     In lines with an uneven number of quotes (without preceding '\')
  1377.          *     we do not know which part to ignore. Therefore we only set
  1378.          *     inquote if the number of quotes in a line is even, unless this
  1379.          *     line or the previous one ends in a '\'.  Complicated, isn't it?
  1380.          */
  1381.         switch (c = linep[pos.col])
  1382.         {
  1383.         case NUL:
  1384.                 /* at end of line without trailing backslash, reset inquote */
  1385.             if (pos.col == 0 || linep[pos.col - 1] != '\\')
  1386.             {
  1387.                 inquote = FALSE;
  1388.                 start_in_quotes = FALSE;
  1389.             }
  1390.             break;
  1391.  
  1392.         case '"':
  1393.                 /* a quote that is preceded with a backslash is ignored */
  1394.             if (do_quotes && (pos.col == 0 || linep[pos.col - 1] != '\\'))
  1395.             {
  1396.                 inquote = !inquote;
  1397.                 start_in_quotes = FALSE;
  1398.             }
  1399.             break;
  1400.  
  1401.         /*
  1402.          * If smart matching ('cpoptions' does not contain '%'):
  1403.          *     Skip things in single quotes: 'x' or '\x'.  Be careful for single
  1404.          *     single quotes, eg jon's.  Things like '\233' or '\x3f' are not
  1405.          *     skipped, there is never a brace in them.
  1406.          */
  1407.         case '\'':
  1408.             if (vim_strchr(p_cpo, CPO_MATCH) == NULL)
  1409.             {
  1410.                 if (idx)                        /* backward search */
  1411.                 {
  1412.                     if (pos.col > 1)
  1413.                     {
  1414.                         if (linep[pos.col - 2] == '\'')
  1415.                             pos.col -= 2;
  1416.                         else if (linep[pos.col - 2] == '\\' &&
  1417.                                     pos.col > 2 && linep[pos.col - 3] == '\'')
  1418.                             pos.col -= 3;
  1419.                     }
  1420.                 }
  1421.                 else if (linep[pos.col + 1])    /* forward search */
  1422.                 {
  1423.                     if (linep[pos.col + 1] == '\\' &&
  1424.                             linep[pos.col + 2] && linep[pos.col + 3] == '\'')
  1425.                         pos.col += 3;
  1426.                     else if (linep[pos.col + 2] == '\'')
  1427.                         pos.col += 2;
  1428.                 }
  1429.             }
  1430.             break;
  1431.  
  1432.         default:
  1433.                     /* Check for match outside of quotes, and inside of
  1434.                      * quotes when the start is also inside of quotes */
  1435.             if (!inquote || start_in_quotes == TRUE)
  1436.             {
  1437.                 if (c == initc)
  1438.                     count++;
  1439.                 else if (c == findc)
  1440.                 {
  1441.                     if (count == 0)
  1442.                         return &pos;
  1443.                     count--;
  1444.                 }
  1445.             }
  1446.         }
  1447.     }
  1448.  
  1449.     if (comment_dir == -1 && count > 0)
  1450.     {
  1451.         pos = match_pos;
  1452.         return &pos;
  1453.     }
  1454.     return (FPOS *) NULL;        /* never found it */
  1455. }
  1456.  
  1457. /*
  1458.  * Move cursor briefly to character matching the one under the cursor.
  1459.  * Show the match only if it is visible on the screen.
  1460.  */
  1461.     void
  1462. showmatch()
  1463. {
  1464.     FPOS           *lpos, csave;
  1465.     colnr_t            vcol;
  1466.  
  1467.     if ((lpos = findmatch(NUL)) == NULL)        /* no match, so beep */
  1468.         beep_flush();
  1469.     else if (lpos->lnum >= curwin->w_topline)
  1470.     {
  1471.         if (!curwin->w_p_wrap)
  1472.             getvcol(curwin, lpos, NULL, &vcol, NULL);
  1473.         if (curwin->w_p_wrap || (vcol >= curwin->w_leftcol &&
  1474.                                           vcol < curwin->w_leftcol + Columns))
  1475.         {
  1476.             updateScreen(VALID_TO_CURSCHAR); /* show the new char first */
  1477.             csave = curwin->w_cursor;
  1478.             curwin->w_cursor = *lpos;    /* move to matching char */
  1479.             cursupdate();
  1480.             showruler(0);
  1481.             setcursor();
  1482.             cursor_on();                /* make sure that the cursor is shown */
  1483.             flushbuf();
  1484.  
  1485.             /*
  1486.              * brief pause, unless 'm' is present in 'cpo' and a character is
  1487.              * available.
  1488.              */
  1489.             if (vim_strchr(p_cpo, CPO_SHOWMATCH) != NULL)
  1490.                 mch_delay(500L, TRUE);
  1491.             else if (!char_avail())
  1492.                 mch_delay(500L, FALSE);
  1493.             curwin->w_cursor = csave;    /* restore cursor position */
  1494.             cursupdate();
  1495.         }
  1496.     }
  1497. }
  1498.  
  1499. /*
  1500.  * findsent(dir, count) - Find the start of the next sentence in direction
  1501.  * 'dir' Sentences are supposed to end in ".", "!" or "?" followed by white
  1502.  * space or a line break. Also stop at an empty line.
  1503.  * Return OK if the next sentence was found.
  1504.  */
  1505.     int
  1506. findsent(dir, count)
  1507.     int        dir;
  1508.     long    count;
  1509. {
  1510.     FPOS            pos, tpos;
  1511.     register int    c;
  1512.     int                (*func) __PARMS((FPOS *));
  1513.     int                startlnum;
  1514.     int                noskip = FALSE;            /* do not skip blanks */
  1515.  
  1516.     pos = curwin->w_cursor;
  1517.     if (dir == FORWARD)
  1518.         func = incl;
  1519.     else
  1520.         func = decl;
  1521.  
  1522.     while (count--)
  1523.     {
  1524.         /*
  1525.          * if on an empty line, skip upto a non-empty line
  1526.          */
  1527.         if (gchar(&pos) == NUL)
  1528.         {
  1529.             do
  1530.                 if ((*func)(&pos) == -1)
  1531.                     break;
  1532.             while (gchar(&pos) == NUL);
  1533.             if (dir == FORWARD)
  1534.                 goto found;
  1535.         }
  1536.         /*
  1537.          * if on the start of a paragraph or a section and searching forward,
  1538.          * go to the next line
  1539.          */
  1540.         else if (dir == FORWARD && pos.col == 0 &&
  1541.                                                 startPS(pos.lnum, NUL, FALSE))
  1542.         {
  1543.             if (pos.lnum == curbuf->b_ml.ml_line_count)
  1544.                 return FAIL;
  1545.             ++pos.lnum;
  1546.             goto found;
  1547.         }
  1548.         else if (dir == BACKWARD)
  1549.             decl(&pos);
  1550.  
  1551.         /* go back to the previous non-blank char */
  1552.         while ((c = gchar(&pos)) == ' ' || c == '\t' ||
  1553.              (dir == BACKWARD && vim_strchr((char_u *)".!?)]\"'", c) != NULL))
  1554.         {
  1555.             if (decl(&pos) == -1)
  1556.                 break;
  1557.             /* when going forward: Stop in front of empty line */
  1558.             if (lineempty(pos.lnum) && dir == FORWARD)
  1559.             {
  1560.                 incl(&pos);
  1561.                 goto found;
  1562.             }
  1563.         }
  1564.  
  1565.         /* remember the line where the search started */
  1566.         startlnum = pos.lnum;
  1567.  
  1568.         for (;;)                /* find end of sentence */
  1569.         {
  1570.             c = gchar(&pos);
  1571.             if (c == NUL || (pos.col == 0 && startPS(pos.lnum, NUL, FALSE)))
  1572.             {
  1573.                 if (dir == BACKWARD && pos.lnum != startlnum)
  1574.                     ++pos.lnum;
  1575.                 break;
  1576.             }
  1577.             if (c == '.' || c == '!' || c == '?')
  1578.             {
  1579.                 tpos = pos;
  1580.                 do
  1581.                     if ((c = inc(&tpos)) == -1)
  1582.                         break;
  1583.                 while (vim_strchr((char_u *)")]\"'", c = gchar(&tpos)) != NULL);
  1584.                 if (c == -1  || c == ' ' || c == '\t' || c == NUL)
  1585.                 {
  1586.                     pos = tpos;
  1587.                     if (gchar(&pos) == NUL) /* skip NUL at EOL */
  1588.                         inc(&pos);
  1589.                     break;
  1590.                 }
  1591.             }
  1592.             if ((*func)(&pos) == -1)
  1593.             {
  1594.                 if (count)
  1595.                     return FAIL;
  1596.                 noskip = TRUE;
  1597.                 break;
  1598.             }
  1599.         }
  1600. found:
  1601.             /* skip white space */
  1602.         while (!noskip && ((c = gchar(&pos)) == ' ' || c == '\t'))
  1603.             if (incl(&pos) == -1)
  1604.                 break;
  1605.     }
  1606.  
  1607.     setpcmark();
  1608.     curwin->w_cursor = pos;
  1609.     return OK;
  1610. }
  1611.  
  1612. /*
  1613.  * findpar(dir, count, what) - Find the next paragraph in direction 'dir'
  1614.  * Paragraphs are currently supposed to be separated by empty lines.
  1615.  * Return TRUE if the next paragraph was found.
  1616.  * If 'what' is '{' or '}' we go to the next section.
  1617.  * If 'both' is TRUE also stop at '}'.
  1618.  */
  1619.     int
  1620. findpar(dir, count, what, both)
  1621.     register int    dir;
  1622.     long            count;
  1623.     int                what;
  1624.     int                both;
  1625. {
  1626.     register linenr_t    curr;
  1627.     int                    did_skip;        /* TRUE after separating lines have
  1628.                                                 been skipped */
  1629.     int                    first;            /* TRUE on first line */
  1630.  
  1631.     curr = curwin->w_cursor.lnum;
  1632.  
  1633.     while (count--)
  1634.     {
  1635.         did_skip = FALSE;
  1636.         for (first = TRUE; ; first = FALSE)
  1637.         {
  1638.             if (*ml_get(curr) != NUL)
  1639.                 did_skip = TRUE;
  1640.  
  1641.             if (!first && did_skip && startPS(curr, what, both))
  1642.                 break;
  1643.  
  1644.             if ((curr += dir) < 1 || curr > curbuf->b_ml.ml_line_count)
  1645.             {
  1646.                 if (count)
  1647.                     return FALSE;
  1648.                 curr -= dir;
  1649.                 break;
  1650.             }
  1651.         }
  1652.     }
  1653.     setpcmark();
  1654.     if (both && *ml_get(curr) == '}')    /* include line with '}' */
  1655.         ++curr;
  1656.     curwin->w_cursor.lnum = curr;
  1657.     if (curr == curbuf->b_ml.ml_line_count)
  1658.     {
  1659.         if ((curwin->w_cursor.col = STRLEN(ml_get(curr))) != 0)
  1660.         {
  1661.             --curwin->w_cursor.col;
  1662.             op_inclusive = TRUE;
  1663.         }
  1664.     }
  1665.     else
  1666.         curwin->w_cursor.col = 0;
  1667.     return TRUE;
  1668. }
  1669.  
  1670. /*
  1671.  * check if the string 's' is a nroff macro that is in option 'opt'
  1672.  */
  1673.     static int
  1674. inmacro(opt, s)
  1675.     char_u            *opt;
  1676.     register char_u *s;
  1677. {
  1678.     register char_u *macro;
  1679.  
  1680.     for (macro = opt; macro[0]; ++macro)
  1681.     {
  1682.         if (macro[0] == s[0] && (((s[1] == NUL || s[1] == ' ') &&
  1683.                    (macro[1] == NUL || macro[1] == ' ')) || macro[1] == s[1]))
  1684.             break;
  1685.         ++macro;
  1686.         if (macro[0] == NUL)
  1687.             break;
  1688.     }
  1689.     return (macro[0] != NUL);
  1690. }
  1691.  
  1692. /*
  1693.  * startPS: return TRUE if line 'lnum' is the start of a section or paragraph.
  1694.  * If 'para' is '{' or '}' only check for sections.
  1695.  * If 'both' is TRUE also stop at '}'
  1696.  */
  1697.     int
  1698. startPS(lnum, para, both)
  1699.     linenr_t    lnum;
  1700.     int            para;
  1701.     int            both;
  1702. {
  1703.     register char_u *s;
  1704.  
  1705.     s = ml_get(lnum);
  1706.     if (*s == para || *s == '\f' || (both && *s == '}'))
  1707.         return TRUE;
  1708.     if (*s == '.' && (inmacro(p_sections, s + 1) ||
  1709.                                            (!para && inmacro(p_para, s + 1))))
  1710.         return TRUE;
  1711.     return FALSE;
  1712. }
  1713.  
  1714. /*
  1715.  * The following routines do the word searches performed by the 'w', 'W',
  1716.  * 'b', 'B', 'e', and 'E' commands.
  1717.  */
  1718.  
  1719. /*
  1720.  * To perform these searches, characters are placed into one of three
  1721.  * classes, and transitions between classes determine word boundaries.
  1722.  *
  1723.  * The classes are:
  1724.  *
  1725.  * 0 - white space
  1726.  * 1 - keyword charactes (letters, digits and underscore)
  1727.  * 2 - everything else
  1728.  */
  1729.  
  1730. static int        stype;            /* type of the word motion being performed */
  1731.  
  1732. /*
  1733.  * cls() - returns the class of character at curwin->w_cursor
  1734.  *
  1735.  * The 'type' of the current search modifies the classes of characters if a
  1736.  * 'W', 'B', or 'E' motion is being done. In this case, chars. from class 2
  1737.  * are reported as class 1 since only white space boundaries are of interest.
  1738.  */
  1739.     static int
  1740. cls()
  1741. {
  1742.     register int c;
  1743.  
  1744.     c = gchar_cursor();
  1745.     if (c == ' ' || c == '\t' || c == NUL)
  1746.         return 0;
  1747.  
  1748.     if (iswordchar(c))
  1749.         return 1;
  1750.  
  1751.     /*
  1752.      * If stype is non-zero, report these as class 1.
  1753.      */
  1754.     return (stype == 0) ? 2 : 1;
  1755. }
  1756.  
  1757.  
  1758. /*
  1759.  * fwd_word(count, type, eol) - move forward one word
  1760.  *
  1761.  * Returns FAIL if the cursor was already at the end of the file.
  1762.  * If eol is TRUE, last word stops at end of line (for operators).
  1763.  */
  1764.     int
  1765. fwd_word(count, type, eol)
  1766.     long        count;
  1767.     int            type;
  1768.     int            eol;
  1769. {
  1770.     int            sclass;        /* starting class */
  1771.     int            i;
  1772.     int            last_line;
  1773.  
  1774.     stype = type;
  1775.     while (--count >= 0)
  1776.     {
  1777.         sclass = cls();
  1778.  
  1779.         /*
  1780.          * We always move at least one character, unless on the last character
  1781.          * in the buffer.
  1782.          */
  1783.         last_line = (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count);
  1784.         i = inc_cursor();
  1785.         if (i == -1 || (i == 1 && last_line))
  1786.                                             /* started at last char in file */
  1787.             return FAIL;
  1788.         if (i == 1 && eol && count == 0)    /* started at last char in line */
  1789.             return OK;
  1790.  
  1791.         /*
  1792.          * Go one char past end of current word (if any)
  1793.          */
  1794.         if (sclass != 0)
  1795.             while (cls() == sclass)
  1796.             {
  1797.                 i = inc_cursor();
  1798.                 if (i == -1 || (i == 1 && eol && count == 0))
  1799.                     return OK;
  1800.             }
  1801.  
  1802.         /*
  1803.          * go to next non-white
  1804.          */
  1805.         while (cls() == 0)
  1806.         {
  1807.             /*
  1808.              * We'll stop if we land on a blank line
  1809.              */
  1810.             if (curwin->w_cursor.col == 0 && *ml_get_curline() == NUL)
  1811.                 break;
  1812.  
  1813.             i = inc_cursor();
  1814.             if (i == -1 || (i == 1 && eol && count == 0))
  1815.                 return OK;
  1816.         }
  1817.     }
  1818.     return OK;
  1819. }
  1820.  
  1821. /*
  1822.  * bck_word() - move backward 'count' words
  1823.  *
  1824.  * If stop is TRUE and we are already on the start of a word, move one less.
  1825.  *
  1826.  * Returns FAIL if top of the file was reached.
  1827.  */
  1828.     int
  1829. bck_word(count, type, stop)
  1830.     long        count;
  1831.     int            type;
  1832.     int            stop;
  1833. {
  1834.     int            sclass;        /* starting class */
  1835.  
  1836.     stype = type;
  1837.     while (--count >= 0)
  1838.     {
  1839.         sclass = cls();
  1840.         if (dec_cursor() == -1)        /* started at start of file */
  1841.             return FAIL;
  1842.  
  1843.         if (!stop || sclass == cls() || sclass == 0)
  1844.         {
  1845.             /*
  1846.              * Skip white space before the word.
  1847.              * Stop on an empty line.
  1848.              */
  1849.             while (cls() == 0)
  1850.             {
  1851.                 if (curwin->w_cursor.col == 0 &&
  1852.                                              lineempty(curwin->w_cursor.lnum))
  1853.                     goto finished;
  1854.  
  1855.                 if (dec_cursor() == -1)        /* hit start of file, stop here */
  1856.                     return OK;
  1857.             }
  1858.  
  1859.             /*
  1860.              * Move backward to start of this word.
  1861.              */
  1862.             if (skip_chars(cls(), BACKWARD))
  1863.                 return OK;
  1864.         }
  1865.  
  1866.         inc_cursor();                     /* overshot - forward one */
  1867. finished:
  1868.         stop = FALSE;
  1869.     }
  1870.     return OK;
  1871. }
  1872.  
  1873. /*
  1874.  * end_word() - move to the end of the word
  1875.  *
  1876.  * There is an apparent bug in the 'e' motion of the real vi. At least on the
  1877.  * System V Release 3 version for the 80386. Unlike 'b' and 'w', the 'e'
  1878.  * motion crosses blank lines. When the real vi crosses a blank line in an
  1879.  * 'e' motion, the cursor is placed on the FIRST character of the next
  1880.  * non-blank line. The 'E' command, however, works correctly. Since this
  1881.  * appears to be a bug, I have not duplicated it here.
  1882.  *
  1883.  * Returns FAIL if end of the file was reached.
  1884.  *
  1885.  * If stop is TRUE and we are already on the end of a word, move one less.
  1886.  * If empty is TRUE stop on an empty line.
  1887.  */
  1888.     int
  1889. end_word(count, type, stop, empty)
  1890.     long        count;
  1891.     int            type;
  1892.     int            stop;
  1893.     int            empty;
  1894. {
  1895.     int            sclass;        /* starting class */
  1896.  
  1897.     stype = type;
  1898.     while (--count >= 0)
  1899.     {
  1900.         sclass = cls();
  1901.         if (inc_cursor() == -1)
  1902.             return FAIL;
  1903.  
  1904.         /*
  1905.          * If we're in the middle of a word, we just have to move to the end
  1906.          * of it.
  1907.          */
  1908.         if (cls() == sclass && sclass != 0)
  1909.         {
  1910.             /*
  1911.              * Move forward to end of the current word
  1912.              */
  1913.             if (skip_chars(sclass, FORWARD))
  1914.                 return FAIL;
  1915.         }
  1916.         else if (!stop || sclass == 0)
  1917.         {
  1918.             /*
  1919.              * We were at the end of a word. Go to the end of the next word.
  1920.              * First skip white space, if 'empty' is TRUE, stop at empty line.
  1921.              */
  1922.             while (cls() == 0)
  1923.             {
  1924.                 if (empty && curwin->w_cursor.col == 0 &&
  1925.                                              lineempty(curwin->w_cursor.lnum))
  1926.                     goto finished;
  1927.                 if (inc_cursor() == -1)        /* hit end of file, stop here */
  1928.                     return FAIL;
  1929.             }
  1930.  
  1931.             /*
  1932.              * Move forward to the end of this word.
  1933.              */
  1934.             if (skip_chars(cls(), FORWARD))
  1935.                 return FAIL;
  1936.         }
  1937.         dec_cursor();                    /* overshot - one char backward */
  1938. finished:
  1939.         stop = FALSE;                    /* we move only one word less */
  1940.     }
  1941.     return OK;
  1942. }
  1943.  
  1944. /*
  1945.  * bckend_word(count, type) - move back to the end of the word
  1946.  *
  1947.  * If 'eol' is TRUE, stop at end of line.
  1948.  *
  1949.  * Returns FAIL if start of the file was reached.
  1950.  */
  1951.     int
  1952. bckend_word(count, type, eol)
  1953.     long        count;
  1954.     int            type;
  1955.     int            eol;
  1956. {
  1957.     int            sclass;        /* starting class */
  1958.     int            i;
  1959.  
  1960.     stype = type;
  1961.     while (--count >= 0)
  1962.     {
  1963.         sclass = cls();
  1964.         if ((i = dec_cursor()) == -1)
  1965.             return FAIL;
  1966.         if (eol && i == 1)
  1967.             return OK;
  1968.  
  1969.         /*
  1970.          * Move backward to before the start of this word.
  1971.          */
  1972.         if (sclass != 0)
  1973.         {
  1974.             while (cls() == sclass)
  1975.                 if ((i = dec_cursor()) == -1 || (eol && i == 1))
  1976.                     return OK;
  1977.         }
  1978.  
  1979.         /*
  1980.          * Move backward to end of the previous word
  1981.          */
  1982.         while (cls() == 0)
  1983.         {
  1984.             if (curwin->w_cursor.col == 0 && lineempty(curwin->w_cursor.lnum))
  1985.                 break;
  1986.             if ((i = dec_cursor()) == -1 || (eol && i == 1))
  1987.                 return OK;
  1988.         }
  1989.     }
  1990.     return OK;
  1991. }
  1992.  
  1993.     static int
  1994. skip_chars(cclass, dir)
  1995.     int        cclass;
  1996.     int        dir;
  1997. {
  1998.     while (cls() == cclass)
  1999.         if ((dir == FORWARD ? inc_cursor() : dec_cursor()) == -1)
  2000.             return TRUE;
  2001.     return FALSE;
  2002. }
  2003.  
  2004. /*
  2005.  * Go back to the start of the word or the start of white space
  2006.  */
  2007.     static void
  2008. back_in_line()
  2009. {
  2010.     int            sclass;                /* starting class */
  2011.  
  2012.     sclass = cls();
  2013.     for (;;)
  2014.     {
  2015.         if (curwin->w_cursor.col == 0)        /* stop at start of line */
  2016.             break;
  2017.         --curwin->w_cursor.col;
  2018.         if (cls() != sclass)                /* stop at start of word */
  2019.         {
  2020.             ++curwin->w_cursor.col;
  2021.             break;
  2022.         }
  2023.     }
  2024. }
  2025.  
  2026. /*
  2027.  * Find word under cursor, cursor at end
  2028.  */
  2029.     int
  2030. current_word(count, type)
  2031.     long        count;
  2032.     int            type;
  2033. {
  2034.     FPOS        start;
  2035.     FPOS        pos;
  2036.     int            inclusive = TRUE;
  2037.  
  2038.     stype = type;
  2039.  
  2040.     /*
  2041.      * When visual area is bigger than one character: Extend it.
  2042.      */
  2043.     if (VIsual_active && !equal(curwin->w_cursor, VIsual))
  2044.     {
  2045.         if (lt(curwin->w_cursor, VIsual))
  2046.         {
  2047.             if (decl(&curwin->w_cursor) == -1)
  2048.                 return FAIL;
  2049.             if (cls() == 0)
  2050.             {
  2051.                 if (bckend_word(count, type, TRUE) == FAIL)
  2052.                     return FAIL;
  2053.                 (void)incl(&curwin->w_cursor);
  2054.             }
  2055.             else
  2056.             {
  2057.                 if (bck_word(count, type, TRUE) == FAIL)
  2058.                     return FAIL;
  2059.             }
  2060.         }
  2061.         else
  2062.         {
  2063.             if (incl(&curwin->w_cursor) == -1)
  2064.                 return FAIL;
  2065.             if (cls() == 0)
  2066.             {
  2067.                 if (fwd_word(count, type, TRUE) == FAIL)
  2068.                     return FAIL;
  2069.                 (void)oneleft();
  2070.             }
  2071.             else
  2072.             {
  2073.                 if (end_word(count, type, TRUE, TRUE) == FAIL)
  2074.                     return FAIL;
  2075.             }
  2076.         }
  2077.         return OK;
  2078.     }
  2079.  
  2080.     /*
  2081.      * Go to start of current word or white space.
  2082.      */
  2083.     back_in_line();
  2084.     start = curwin->w_cursor;
  2085.  
  2086.     /*
  2087.      * If the start is on white space, find end of word.
  2088.      * Otherwise find start of next word.
  2089.      */
  2090.     if (cls() == 0)
  2091.     {
  2092.         if (end_word(count, type, TRUE, TRUE) == FAIL)
  2093.             return FAIL;
  2094.     }
  2095.     else
  2096.     {
  2097.         if (fwd_word(count, type, TRUE) == FAIL)
  2098.             return FAIL;
  2099.         /*
  2100.          * If end is just past a new-line, we don't want to include the first
  2101.          * character on the line
  2102.          */
  2103.         if (oneleft() == FAIL)            /* put cursor on last char of area */
  2104.             inclusive = FALSE;
  2105.         else
  2106.         {
  2107.             pos = curwin->w_cursor;        /* save cursor position */
  2108.             /*
  2109.              * If we don't include white space at the end, move the start to 
  2110.              * include some white space there. This makes "d." work better on
  2111.              * the last word in a sentence. Don't delete white space at start
  2112.              * of line (indent).
  2113.              */
  2114.             if (cls() != 0)
  2115.             {
  2116.                 curwin->w_cursor = start;
  2117.                 if (oneleft() == OK)
  2118.                 {
  2119.                     back_in_line();
  2120.                     if (cls() == 0 && curwin->w_cursor.col > 0)
  2121.                         start = curwin->w_cursor;
  2122.                 }
  2123.             }
  2124.             curwin->w_cursor = pos;        /* put cursor back at end */
  2125.         }
  2126.     }
  2127.     if (VIsual_active)
  2128.     {
  2129.         /* should do something when inclusive == FALSE ! */
  2130.         VIsual = start;
  2131.         VIsual_mode = 'v';
  2132.         update_curbuf(NOT_VALID);        /* update the inversion */
  2133.     }
  2134.     else
  2135.     {
  2136.         curbuf->b_op_start = start;
  2137.         op_motion_type = MCHAR;
  2138.         op_inclusive = inclusive;
  2139.     }
  2140.     return OK;
  2141. }
  2142.  
  2143. /*
  2144.  * Find sentence under the cursor, cursor at end.
  2145.  */
  2146.     int
  2147. current_sent(count)
  2148.     long    count;
  2149. {
  2150.     FPOS    start;
  2151.     FPOS    pos;
  2152.     int        start_blank;
  2153.     int        c;
  2154.  
  2155.     pos = curwin->w_cursor;
  2156.     start = pos;
  2157.  
  2158.     /*
  2159.      * When visual area is bigger than one character: Extend it.
  2160.      */
  2161.     if (VIsual_active && !equal(curwin->w_cursor, VIsual))
  2162.     {
  2163.         if (lt(pos, VIsual))
  2164.         {
  2165.             /*
  2166.              * Do a "sentence backward" on the next character.
  2167.              * If we end up on the same position, we were already at the start
  2168.              * of a sentence
  2169.              */
  2170.             if (incl(&curwin->w_cursor) == -1)
  2171.                 return FAIL;
  2172.             findsent(BACKWARD, 1L);
  2173.             start = curwin->w_cursor;
  2174.             if (count > 1)
  2175.                 findsent(BACKWARD, count - 1);
  2176.             /*
  2177.              * When at start of sentence: Include blanks in front of sentence.
  2178.              * Use current_word() to cross line boundaries.
  2179.              * If we don't end up on a blank or on an empty line, go back to
  2180.              * the start of the previous sentence.
  2181.              */
  2182.             if (equal(pos, start))
  2183.             {
  2184.                 current_word(1L, 0);
  2185.                 c = gchar_cursor();
  2186.                 if (c != NUL && !vim_iswhite(c))
  2187.                     findsent(BACKWARD, 1L);
  2188.             }
  2189.  
  2190.         }
  2191.         else
  2192.         {
  2193.             /*
  2194.              * When one char before start of sentence: Don't include blanks in
  2195.              * front of next sentence.
  2196.              * Else go count sentences forward.
  2197.              */
  2198.             findsent(FORWARD, 1L);
  2199.             incl(&pos);
  2200.             if (equal(pos, curwin->w_cursor))
  2201.             {
  2202.                 findsent(FORWARD, count);
  2203.                 find_first_blank(&curwin->w_cursor);
  2204.             }
  2205.             else if (count > 1)
  2206.                 findsent(FORWARD, count - 1);
  2207.             decl(&curwin->w_cursor);
  2208.         }
  2209.         return OK;
  2210.     }
  2211.  
  2212.     /*
  2213.      * Find start of next sentence.
  2214.      */
  2215.     findsent(FORWARD, 1L);
  2216.  
  2217.     /*
  2218.      * If cursor started on blank, check if it is just before the start of the
  2219.      * next sentence.
  2220.      */
  2221.     while (vim_iswhite(gchar(&pos)))
  2222.         incl(&pos);
  2223.     if (equal(pos, curwin->w_cursor))
  2224.     {
  2225.         start_blank = TRUE;
  2226.         /*
  2227.          * go back to first blank
  2228.          */
  2229.         while (decl(&start) != -1)
  2230.         {
  2231.             if (!vim_iswhite(gchar(&start)))
  2232.             {
  2233.                 incl(&start);
  2234.                 break;
  2235.             }
  2236.         }
  2237.     }
  2238.     else
  2239.     {
  2240.         start_blank = FALSE;
  2241.         findsent(BACKWARD, 1L);
  2242.         start = curwin->w_cursor;
  2243.     }
  2244.     findsent(FORWARD, count);
  2245.  
  2246.     /*
  2247.      * If the blank in front of the sentence is included, exclude the blanks
  2248.      * at the end of the sentence, go back to the first blank.
  2249.      */
  2250.     if (start_blank)
  2251.         find_first_blank(&curwin->w_cursor);
  2252.     else
  2253.     {
  2254.         /*
  2255.          * If there are no trailing blanks, try to include leading blanks
  2256.          */
  2257.         pos = curwin->w_cursor;
  2258.         decl(&pos);
  2259.         if (!vim_iswhite(gchar(&pos)))
  2260.             find_first_blank(&start);
  2261.     }
  2262.  
  2263.     if (VIsual_active)
  2264.     {
  2265.         VIsual = start;
  2266.         VIsual_mode = 'v';
  2267.         decl(&curwin->w_cursor);        /* don't include the cursor char */
  2268.         update_curbuf(NOT_VALID);        /* update the inversion */
  2269.     }
  2270.     else
  2271.     {
  2272.         curbuf->b_op_start = start;
  2273.         op_motion_type = MCHAR;
  2274.         op_inclusive = FALSE;
  2275.     }
  2276.     return OK;
  2277. }
  2278.  
  2279.     int
  2280. current_block(what, count)
  2281.     int        what;            /* '(' or '{' */
  2282.     long    count;
  2283. {
  2284.     FPOS    old_pos;
  2285.     FPOS    *pos = NULL;
  2286.     FPOS    start_pos;
  2287.     FPOS    *end_pos;
  2288.     FPOS    old_start, old_end;
  2289.     int        inclusive = FALSE;
  2290.     int        other;
  2291.  
  2292.     old_pos = curwin->w_cursor;
  2293.     if (what == '{')
  2294.         other = '}';
  2295.     else
  2296.         other = ')';
  2297.  
  2298.     old_end = curwin->w_cursor;                /* remember where we started */
  2299.     old_start = old_end;
  2300.  
  2301.     /*
  2302.      * If we start on '(', '{', ')' or '}', use the whole block inclusive.
  2303.      */
  2304.     if (!VIsual_active || (VIsual.lnum == curwin->w_cursor.lnum &&
  2305.                                           VIsual.col == curwin->w_cursor.col))
  2306.     {
  2307.         if (what == '{')                    /* ignore indent */
  2308.             while (inindent(1))
  2309.                 if (inc_cursor() != 0)
  2310.                     break;
  2311.         if (gchar_cursor() == what)            /* cursor on '(' or '{' */
  2312.         {
  2313.             ++curwin->w_cursor.col;
  2314.             inclusive = TRUE;
  2315.         }
  2316.         else if (gchar_cursor() == other)    /* cursor on ')' or '}' */
  2317.             inclusive = TRUE;
  2318.     }
  2319.     else if (lt(VIsual, curwin->w_cursor))
  2320.     {
  2321.         old_start = VIsual;
  2322.         curwin->w_cursor = VIsual;            /* cursor at low end of Visual */
  2323.     }
  2324.     else
  2325.         old_end = VIsual;
  2326.  
  2327.     /*
  2328.      * Search backwards for unclosed '(' or '{'.
  2329.      * put this position in start_pos.
  2330.      */
  2331.     while (count--)
  2332.     {
  2333.         if ((pos = findmatch(what)) == NULL)
  2334.             break;
  2335.         curwin->w_cursor = *pos;
  2336.         start_pos = *pos;    /* the findmatch for end_pos will overwrite *pos */
  2337.     }
  2338.  
  2339.     /*
  2340.      * Search for matching ')' or '}'.
  2341.      * Put this position in curwin->w_cursor.
  2342.      */
  2343.     if (pos == NULL || (end_pos = findmatch(other)) == NULL)
  2344.     {
  2345.         curwin->w_cursor = old_pos;
  2346.         return FAIL;
  2347.     }
  2348.     curwin->w_cursor = *end_pos;
  2349.  
  2350.     /*
  2351.      * Try to exclude the '(', '{', ')' and '}'.
  2352.      * If the ending '}' is only preceded by indent, skip that indent.
  2353.      * But only if the resulting area is not smaller than what we started with.
  2354.      */
  2355.     if (!inclusive)
  2356.     {
  2357.         incl(&start_pos);
  2358.         old_pos = curwin->w_cursor;
  2359.         decl(&curwin->w_cursor);
  2360.         if (what == '{')
  2361.             while (inindent(0))
  2362.                 if (decl(&curwin->w_cursor) != 0)
  2363.                     break;
  2364.         if (!lt(start_pos, old_start) && !lt(old_end, curwin->w_cursor))
  2365.         {
  2366.             decl(&start_pos);
  2367.             curwin->w_cursor = old_pos;
  2368.         }
  2369.     }
  2370.  
  2371.     if (VIsual_active)
  2372.     {
  2373.         VIsual = start_pos;
  2374.         VIsual_mode = 'v';
  2375.         update_curbuf(NOT_VALID);        /* update the inversion */
  2376.         showmode();
  2377.     }
  2378.     else
  2379.     {
  2380.         curbuf->b_op_start = start_pos;
  2381.         op_motion_type = MCHAR;
  2382.         op_inclusive = TRUE;
  2383.     }
  2384.  
  2385.     return OK;
  2386. }
  2387.  
  2388.     int
  2389. current_par(type, count)
  2390.     int        type;            /* 'p' for paragraph, 'S' for section */
  2391.     long    count;
  2392. {
  2393.     linenr_t    start;
  2394.     linenr_t    end;
  2395.     int            white_in_front;
  2396.     int            dir;
  2397.     int            start_is_white;
  2398.     int            retval = OK;
  2399.  
  2400.     if (type == 'S')        /* not implemented yet */
  2401.         return FAIL;
  2402.     
  2403.     start = curwin->w_cursor.lnum;
  2404.  
  2405.     /*
  2406.      * When visual area is more than one line: extend it.
  2407.      */
  2408.     if (VIsual_active && start != VIsual.lnum)
  2409.     {
  2410.         if (start < VIsual.lnum)
  2411.             dir = BACKWARD;
  2412.         else
  2413.             dir = FORWARD;
  2414.         while (count--)
  2415.         {
  2416.             if (start == (dir == BACKWARD ? 1 : curbuf->b_ml.ml_line_count))
  2417.                 retval = FAIL;
  2418.  
  2419.             start_is_white = -1;
  2420.             for (;;)
  2421.             {
  2422.                 if (start == (dir == BACKWARD ? 1 : curbuf->b_ml.ml_line_count))
  2423.                     break;
  2424.                 if (start_is_white >= 0 &&
  2425.                                   (start_is_white != linewhite(start + dir) ||
  2426.                                     startPS(start + (dir > 0 ? 1 : 0), 0, 0)))
  2427.                     break;
  2428.                 start += dir;
  2429.                 if (start_is_white < 0)
  2430.                     start_is_white = linewhite(start);
  2431.             }
  2432.         }
  2433.         curwin->w_cursor.lnum = start;
  2434.         curwin->w_cursor.col = 0;
  2435.         return retval;
  2436.     }
  2437.  
  2438.     /*
  2439.      * First move back to the start of the paragraph or white lines
  2440.      */
  2441.     white_in_front = linewhite(start);
  2442.     while (start > 1)
  2443.     {
  2444.         if (white_in_front)            /* stop at first white line */
  2445.         {
  2446.             if (!linewhite(start - 1))
  2447.                 break;
  2448.         }
  2449.         else            /* stop at first non-white line of start of paragraph */
  2450.         {
  2451.             if (linewhite(start - 1) || startPS(start, 0, 0))
  2452.                 break;
  2453.         }
  2454.         --start;
  2455.     }
  2456.  
  2457.     /*
  2458.      * Move past the end of the white lines.
  2459.      */
  2460.     end = start;
  2461.     while (linewhite(end) && end < curbuf->b_ml.ml_line_count)
  2462.         ++end;
  2463.     
  2464.     --end;
  2465.     while (count--)
  2466.     {
  2467.         if (end == curbuf->b_ml.ml_line_count)
  2468.             return FAIL;
  2469.  
  2470.         ++end;
  2471.         /*
  2472.          * skip to end of paragraph
  2473.          */
  2474.         while (end < curbuf->b_ml.ml_line_count &&
  2475.                                !linewhite(end + 1) && !startPS(end + 1, 0, 0))
  2476.             ++end;
  2477.  
  2478.         if (count == 0 && white_in_front)
  2479.             break;
  2480.  
  2481.         /*
  2482.          * skip to end of white lines after paragraph
  2483.          */
  2484.         while (end < curbuf->b_ml.ml_line_count && linewhite(end + 1))
  2485.             ++end;
  2486.     }
  2487.  
  2488.     /*
  2489.      * If there are no empty lines at the end, try to find some empty lines at
  2490.      * the start (unless that has been done already).
  2491.      */
  2492.     if (!white_in_front && !linewhite(end))
  2493.         while (start > 1 && linewhite(start - 1))
  2494.             --start;
  2495.  
  2496.     if (VIsual_active)
  2497.     {
  2498.         VIsual.lnum = start;
  2499.         VIsual_mode = 'V';
  2500.         update_curbuf(NOT_VALID);        /* update the inversion */
  2501.         showmode();
  2502.     }
  2503.     else
  2504.     {
  2505.         curbuf->b_op_start.lnum = start;
  2506.         op_motion_type = MLINE;
  2507.     }
  2508.     curwin->w_cursor.lnum = end;
  2509.     curwin->w_cursor.col = 0;
  2510.  
  2511.     return OK;
  2512. }
  2513.  
  2514. /*
  2515.  * linewhite -- return TRUE if line 'lnum' is empty or has white chars only.
  2516.  */
  2517.     int
  2518. linewhite(lnum)
  2519.     linenr_t    lnum;
  2520. {
  2521.     char_u    *p;
  2522.  
  2523.     p = skipwhite(ml_get(lnum));
  2524.     return (*p == NUL);
  2525. }
  2526.  
  2527.     static void
  2528. find_first_blank(posp)
  2529.     FPOS    *posp;
  2530. {
  2531.     while (decl(posp) != -1)
  2532.     {
  2533.         if (!vim_iswhite(gchar(posp)))
  2534.         {
  2535.             incl(posp);
  2536.             break;
  2537.         }
  2538.     }
  2539. }
  2540.  
  2541.     void
  2542. find_pattern_in_path(ptr, len, whole, skip_comments,
  2543.                                     type, count, action, start_lnum, end_lnum)
  2544.     char_u    *ptr;            /* pointer to search pattern */
  2545.     int        len;            /* length of search pattern */
  2546.     int        whole;            /* match whole words only */
  2547.     int        skip_comments;    /* don't match inside comments */
  2548.     int        type;            /* Type of search; are we looking for a type?  a
  2549.                                 macro? */
  2550.     long    count;
  2551.     int        action;            /* What to do when we find it */
  2552.     linenr_t    start_lnum;    /* first line to start searching */
  2553.     linenr_t    end_lnum;    /* last line for searching */
  2554. {
  2555.     SearchedFile *files;                /* Stack of included files */
  2556.     SearchedFile *bigger;                /* When we need more space */
  2557.     int            max_path_depth = 50;
  2558.     long        match_count = 1;
  2559.  
  2560.     char_u        *pat;
  2561.     char_u        *new_fname;
  2562.     char_u        *curr_fname = curbuf->b_xfilename;
  2563.     char_u        *prev_fname = NULL;
  2564.     linenr_t    lnum;
  2565.     int            depth;
  2566.     int            depth_displayed;        /* For type==CHECK_PATH */
  2567.     int            old_files;
  2568.     int            already_searched;
  2569.     char_u        *file_line;
  2570.     char_u        *line;
  2571.     char_u        *p;
  2572.     char_u        *p2 = NUL;                /* Init for gcc */
  2573.     char_u        save_char = NUL;
  2574.     int            define_matched;
  2575.     struct regexp *prog = NULL;
  2576.     struct regexp *include_prog = NULL;
  2577.     struct regexp *define_prog = NULL;
  2578.     int            matched = FALSE;
  2579.     int            did_show = FALSE;
  2580.     int            found = FALSE;
  2581.     int            i;
  2582.  
  2583.     file_line = alloc(LSIZE);
  2584.     if (file_line == NULL)
  2585.         return;
  2586.  
  2587.     reg_magic = p_magic;
  2588.     if (type != CHECK_PATH)
  2589.     {
  2590.         pat = alloc(len + 5);
  2591.         if (pat == NULL)
  2592.             goto fpip_end;
  2593.         sprintf((char *)pat, whole ? "\\<%.*s\\>" : "%.*s", len, ptr);
  2594.         set_reg_ic(pat);    /* set reg_ic according to p_ic, p_scs and pat */
  2595.         prog = vim_regcomp(pat);
  2596.         vim_free(pat);
  2597.         if (prog == NULL)
  2598.             goto fpip_end;
  2599.     }
  2600.     reg_ic = FALSE;        /* don't ignore case in include and define patterns */
  2601.     if (*p_inc != NUL)
  2602.     {
  2603.         include_prog = vim_regcomp(p_inc);
  2604.         if (include_prog == NULL)
  2605.             goto fpip_end;
  2606.     }
  2607.     if (type == FIND_DEFINE && *p_def != NUL)
  2608.     {
  2609.         define_prog = vim_regcomp(p_def);
  2610.         if (define_prog == NULL)
  2611.             goto fpip_end;
  2612.     }
  2613.     files = (SearchedFile *)lalloc(max_path_depth * sizeof(SearchedFile), TRUE);
  2614.     if (files == NULL)
  2615.         goto fpip_end;
  2616.     for (i = 0; i < max_path_depth; i++)
  2617.     {
  2618.         files[i].fp = NULL;
  2619.         files[i].name = NULL;
  2620.         files[i].lnum = 0;
  2621.         files[i].matched = FALSE;
  2622.     }
  2623.     old_files = max_path_depth;
  2624.     depth = depth_displayed = -1;
  2625.  
  2626.     lnum = start_lnum;
  2627.     if (end_lnum > curbuf->b_ml.ml_line_count)
  2628.         end_lnum = curbuf->b_ml.ml_line_count;
  2629.     if (lnum > end_lnum)                /* do at least one line */
  2630.         lnum = end_lnum;
  2631.     line = ml_get(lnum);
  2632.  
  2633.     for (;;)
  2634.     {
  2635.         if (include_prog != NULL && vim_regexec(include_prog, line, TRUE))
  2636.         {
  2637.             new_fname = get_file_name_in_path(include_prog->endp[0] + 1,
  2638.                                                                 0, FNAME_EXP);
  2639.             already_searched = FALSE;
  2640.             if (new_fname != NULL)
  2641.             {
  2642.                 /* Check whether we have already searched in this file */
  2643.                 for (i = 0;; i++)
  2644.                 {
  2645.                     if (i == depth + 1)
  2646.                         i = old_files;
  2647.                     if (i == max_path_depth)
  2648.                         break;
  2649.                     if (STRCMP(new_fname, files[i].name) == 0)
  2650.                     {
  2651.                         if (type != CHECK_PATH &&
  2652.                                 action == ACTION_SHOW_ALL && files[i].matched)
  2653.                         {
  2654.                             msg_outchar('\n');        /* cursor below last one */
  2655.                             if (!got_int)            /* don't display if 'q'
  2656.                                                        typed at "--more--"
  2657.                                                        mesage */
  2658.                             {
  2659.                                 set_highlight('d');    /* Same as for dirs */
  2660.                                 start_highlight();
  2661.                                 msg_home_replace(new_fname);
  2662.                                 stop_highlight();
  2663.                                 MSG_OUTSTR(" (includes previously listed match)");
  2664.                                 prev_fname = NULL;
  2665.                             }
  2666.                         }
  2667.                         vim_free(new_fname);
  2668.                         new_fname = NULL;
  2669.                         already_searched = TRUE;
  2670.                         break;
  2671.                     }
  2672.                 }
  2673.             }
  2674.  
  2675.             if (type == CHECK_PATH && (action == ACTION_SHOW_ALL ||
  2676.                                     (new_fname == NULL && !already_searched)))
  2677.             {
  2678.                 if (did_show)
  2679.                     msg_outchar('\n');        /* cursor below last one */
  2680.                 else
  2681.                 {
  2682.                     gotocmdline(TRUE);        /* cursor at status line */
  2683.                     set_highlight('t');        /* Highlight title */
  2684.                     start_highlight();
  2685.                     MSG_OUTSTR("--- Included files ");
  2686.                     if (action != ACTION_SHOW_ALL)
  2687.                         MSG_OUTSTR("not found ");
  2688.                     MSG_OUTSTR("in path ---\n");
  2689.                     stop_highlight();
  2690.                 }
  2691.                 did_show = TRUE;
  2692.                 while (depth_displayed < depth && !got_int)
  2693.                 {
  2694.                     ++depth_displayed;
  2695.                     for (i = 0; i < depth_displayed; i++)
  2696.                         MSG_OUTSTR("  ");
  2697.                     msg_home_replace(files[depth_displayed].name);
  2698.                     MSG_OUTSTR(" -->\n");
  2699.                 }
  2700.                 if (!got_int)                /* don't display if 'q' typed
  2701.                                                for "--more--" message */
  2702.                 {
  2703.                     for (i = 0; i <= depth_displayed; i++)
  2704.                         MSG_OUTSTR("  ");
  2705.                     set_highlight('d');        /* Same as for directories */
  2706.                     start_highlight();
  2707.                     /*
  2708.                      * Isolate the file name.
  2709.                      * Include the surrounding "" or <> if present.
  2710.                      */
  2711.                     for (p = include_prog->endp[0] + 1; !isfilechar(*p); p++)
  2712.                         ;
  2713.                     for (i = 0; isfilechar(p[i]); i++)
  2714.                         ;
  2715.                     if (p[-1] == '"' || p[-1] == '<')
  2716.                     {
  2717.                         --p;
  2718.                         ++i;
  2719.                     }
  2720.                     if (p[i] == '"' || p[i] == '>')
  2721.                         ++i;
  2722.                     save_char = p[i];
  2723.                     p[i] = NUL;
  2724.                     msg_outstr(p);
  2725.                     p[i] = save_char;
  2726.                     stop_highlight();
  2727.                     if (new_fname == NULL && action == ACTION_SHOW_ALL)
  2728.                     {
  2729.                         if (already_searched)
  2730.                             MSG_OUTSTR("  (Already listed)");
  2731.                         else
  2732.                             MSG_OUTSTR("  NOT FOUND");
  2733.                     }
  2734.                 }
  2735.                 flushbuf();            /* output each line directly */
  2736.             }
  2737.  
  2738.             if (new_fname != NULL)
  2739.             {
  2740.                 /* Push the new file onto the file stack */
  2741.                 if (depth + 1 == old_files)
  2742.                 {
  2743.                     bigger = (SearchedFile *)lalloc(max_path_depth * 2
  2744.                                                 * sizeof(SearchedFile), TRUE);
  2745.                     if (bigger != NULL)
  2746.                     {
  2747.                         for (i = 0; i <= depth; i++)
  2748.                             bigger[i] = files[i];
  2749.                         for (i = depth + 1; i < old_files + max_path_depth; i++)
  2750.                         {
  2751.                             bigger[i].fp = NULL;
  2752.                             bigger[i].name = NULL;
  2753.                             bigger[i].lnum = 0;
  2754.                             bigger[i].matched = FALSE;
  2755.                         }
  2756.                         for (i = old_files; i < max_path_depth; i++)
  2757.                             bigger[i + max_path_depth] = files[i];
  2758.                         old_files += max_path_depth;
  2759.                         max_path_depth *= 2;
  2760.                         vim_free(files);
  2761.                         files = bigger;
  2762.                     }
  2763.                 }
  2764.                 if ((files[depth + 1].fp = fopen((char *)new_fname, "r"))
  2765.                                                                     == NULL)
  2766.                     vim_free(new_fname);
  2767.                 else
  2768.                 {
  2769.                     if (++depth == old_files)
  2770.                     {
  2771.                         /*
  2772.                          * lalloc() for 'bigger' must have failed above.  We
  2773.                          * will forget one of our already visited files now.
  2774.                          */
  2775.                         vim_free(files[old_files].name);
  2776.                         ++old_files;
  2777.                     }
  2778.                     files[depth].name = curr_fname = new_fname;
  2779.                     files[depth].lnum = 0;
  2780.                     files[depth].matched = FALSE;
  2781.                 }
  2782.             }
  2783.         }
  2784.         else
  2785.         {
  2786.             /*
  2787.              * Check if the line is a define (type == FIND_DEFINE)
  2788.              */
  2789.             p = line;
  2790.             define_matched = FALSE;
  2791.             if (define_prog != NULL && vim_regexec(define_prog, line, TRUE))
  2792.             {
  2793.                 /*
  2794.                  * Pattern must be first identifier after 'define', so skip
  2795.                  * to that position before checking for match of pattern.  Also
  2796.                  * don't let it match beyond the end of this identifier.
  2797.                  */
  2798.                 p = define_prog->endp[0] + 1;
  2799.                 while (*p && !isidchar(*p))
  2800.                     p++;
  2801.                 p2 = p;
  2802.                 while (*p2 && isidchar(*p2))
  2803.                     p2++;
  2804.                 save_char = *p2;
  2805.                 *p2 = NUL;
  2806.                 define_matched = TRUE;
  2807.             }
  2808.  
  2809.             /*
  2810.              * Look for a match.  Don't do this if we are looking for a
  2811.              * define and this line didn't match define_prog above.
  2812.              */
  2813.             if ((define_prog == NULL || define_matched) &&
  2814.                               prog != NULL && vim_regexec(prog, p, p == line))
  2815.             {
  2816.                 matched = TRUE;
  2817.                 /*
  2818.                  * Check if the line is not a comment line (unless we are
  2819.                  * looking for a define).  A line starting with "# define" is
  2820.                  * not considered to be a comment line.
  2821.                  */
  2822.                 if (!define_matched && skip_comments)
  2823.                 {
  2824.                     fo_do_comments = TRUE;
  2825.                     if ((*line != '#' ||
  2826.                             STRNCMP(skipwhite(line + 1), "define", 6) != 0) &&
  2827.                                                    get_leader_len(line, NULL))
  2828.                         matched = FALSE;
  2829.  
  2830.                     /*
  2831.                      * Also check for a "/ *" or "/ /" before the match.
  2832.                      * Skips lines like "int idx;  / * normal index * /" when
  2833.                      * looking for "normal".
  2834.                      */
  2835.                     else
  2836.                         for (p = line; *p && p < prog->startp[0]; ++p)
  2837.                             if (p[0] == '/' && (p[1] == '*' || p[1] == '/'))
  2838.                             {
  2839.                                 matched = FALSE;
  2840.                                 break;
  2841.                             }
  2842.                     fo_do_comments = FALSE;
  2843.                 }
  2844.             }
  2845.             if (define_matched)
  2846.                 *p2 = save_char;
  2847.         }
  2848.         if (matched)
  2849.         {
  2850. #ifdef INSERT_EXPAND
  2851.             if (action == ACTION_EXPAND)
  2852.             {
  2853.                 if (depth == -1 && lnum == curwin->w_cursor.lnum)
  2854.                     break;
  2855.                 found = TRUE;
  2856.                 p = prog->startp[0];
  2857.                 while (iswordchar(*p))
  2858.                     ++p;
  2859.                 if (add_completion_and_infercase(prog->startp[0],
  2860.                                                    (int)(p - prog->startp[0]),
  2861.                         curr_fname == curbuf->b_xfilename ? NULL : curr_fname,
  2862.                                                         FORWARD) == RET_ERROR)
  2863.                     break;
  2864.             }
  2865.             else
  2866. #endif
  2867.                  if (action == ACTION_SHOW_ALL)
  2868.             {
  2869.                 found = TRUE;
  2870.                 if (!did_show)
  2871.                     gotocmdline(TRUE);            /* cursor at status line */
  2872.                 if (curr_fname != prev_fname)
  2873.                 {
  2874.                     if (did_show)
  2875.                         msg_outchar('\n');        /* cursor below last one */
  2876.                     if (!got_int)                /* don't display if 'q' typed
  2877.                                                     at "--more--" mesage */
  2878.                     {
  2879.                         set_highlight('d');        /* Same as for directories */
  2880.                         start_highlight();
  2881.                         msg_home_replace(curr_fname);
  2882.                         stop_highlight();
  2883.                     }
  2884.                     prev_fname = curr_fname;
  2885.                 }
  2886.                 did_show = TRUE;
  2887.                 if (!got_int)
  2888.                     show_pat_in_path(line, type, TRUE, action,
  2889.                             (depth == -1) ? NULL : files[depth].fp,
  2890.                             (depth == -1) ? &lnum : &files[depth].lnum,
  2891.                             match_count++);
  2892.  
  2893.                 /* Set matched flag for this file and all the ones that
  2894.                  * include it */
  2895.                 for (i = 0; i <= depth; ++i)
  2896.                     files[i].matched = TRUE;
  2897.             }
  2898.             else if (--count <= 0)
  2899.             {
  2900.                 found = TRUE;
  2901.                 if (depth == -1 && lnum == curwin->w_cursor.lnum)
  2902.                     EMSG("Match is on current line");
  2903.                 else if (action == ACTION_SHOW)
  2904.                 {
  2905.                     show_pat_in_path(line, type, did_show, action,
  2906.                         (depth == -1) ? NULL : files[depth].fp,
  2907.                         (depth == -1) ? &lnum : &files[depth].lnum, 1L);
  2908.                     did_show = TRUE;
  2909.                 }
  2910.                 else
  2911.                 {
  2912.                     if (action == ACTION_SPLIT)
  2913.                     {
  2914.                         if (win_split(0, FALSE) == FAIL)
  2915.                             break;
  2916.                     }
  2917.                     if (depth == -1)
  2918.                     {
  2919.                         setpcmark();
  2920.                         curwin->w_cursor.lnum = lnum;
  2921.                     }
  2922.                     else
  2923.                         if (getfile(0, files[depth].name, NULL, TRUE,
  2924.                                                         files[depth].lnum) > 0)
  2925.                             break;        /* failed to jump to file */
  2926.                 }
  2927.                 if (action != ACTION_SHOW)
  2928.                 {
  2929.                     curwin->w_cursor.col = prog->startp[0] - line;
  2930.                     curwin->w_set_curswant = TRUE;
  2931.                 }
  2932.                 break;
  2933.             }
  2934.             matched = FALSE;
  2935.         }
  2936.         line_breakcheck();
  2937.         if (got_int)
  2938.             break;
  2939.         while (depth >= 0)
  2940.         {
  2941.             if (!vim_fgets(line = file_line, LSIZE, files[depth].fp))
  2942.             {
  2943.                 ++files[depth].lnum;
  2944.                 break;
  2945.             }
  2946.             fclose(files[depth].fp);
  2947.             --old_files;
  2948.             files[old_files].name = files[depth].name;
  2949.             files[old_files].matched = files[depth].matched;
  2950.             --depth;
  2951.             curr_fname = (depth == -1) ? curbuf->b_xfilename
  2952.                                        : files[depth].name;
  2953.             if (depth < depth_displayed)
  2954.                 depth_displayed = depth;
  2955.         }
  2956.         if (depth < 0)
  2957.         {
  2958.             if (++lnum > end_lnum)
  2959.                 break;
  2960.             line = ml_get(lnum);
  2961.         }
  2962.     }
  2963.     for (i = 0; i <= depth; i++)
  2964.     {
  2965.         fclose(files[i].fp);
  2966.         vim_free(files[i].name);
  2967.     }
  2968.     for (i = old_files; i < max_path_depth; i++)
  2969.         vim_free(files[i].name);
  2970.     vim_free(files);
  2971.  
  2972.     if (type == CHECK_PATH)
  2973.     {
  2974.         if (!did_show)
  2975.         {
  2976.             if (action != ACTION_SHOW_ALL)
  2977.                 MSG("All included files were found");
  2978.             else
  2979.                 MSG("No included files");
  2980.         }
  2981.     }
  2982.     else if (!found
  2983. #ifdef INSERT_EXPAND
  2984.                     && action != ACTION_EXPAND
  2985. #endif
  2986.                                                 )
  2987.     {
  2988.         if (got_int)
  2989.             emsg(e_interr);
  2990.         else if (type == FIND_DEFINE)
  2991.             EMSG("Couldn't find definition");
  2992.         else
  2993.             EMSG("Couldn't find pattern");
  2994.     }
  2995.     if (action == ACTION_SHOW || action == ACTION_SHOW_ALL)
  2996.         msg_end();
  2997.  
  2998. fpip_end:
  2999.     vim_free(file_line);
  3000.     vim_free(prog);
  3001.     vim_free(include_prog);
  3002.     vim_free(define_prog);
  3003. }
  3004.  
  3005.     static void
  3006. show_pat_in_path(line, type, did_show, action, fp, lnum, count)
  3007.     char_u    *line;
  3008.     int        type;
  3009.     int        did_show;
  3010.     int        action;
  3011.     FILE    *fp;
  3012.     linenr_t *lnum;
  3013.     long    count;
  3014. {
  3015.     char_u    *p;
  3016.  
  3017.     if (did_show)
  3018.         msg_outchar('\n');        /* cursor below last one */
  3019.     else
  3020.         gotocmdline(TRUE);        /* cursor at status line */
  3021.     if (got_int)                /* 'q' typed at "--more--" message */
  3022.         return;
  3023.     for (;;)
  3024.     {
  3025.         p = line + STRLEN(line) - 1;
  3026.         if (fp != NULL)
  3027.         {
  3028.             /* We used fgets(), so get rid of newline at end */
  3029.             if (p >= line && *p == '\n')
  3030.                 --p;
  3031.             if (p >= line && *p == '\r')
  3032.                 --p;
  3033.             *(p + 1) = NUL;
  3034.         }
  3035.         if (action == ACTION_SHOW_ALL)
  3036.         {
  3037.             sprintf((char *)IObuff, "%3ld: ", count);    /* show match nr */
  3038.             msg_outstr(IObuff);
  3039.             set_highlight('n');                    /* Highlight line numbers */
  3040.             start_highlight();
  3041.             sprintf((char *)IObuff, "%4ld", *lnum);        /* show line nr */
  3042.             msg_outstr(IObuff);
  3043.             stop_highlight();
  3044.             MSG_OUTSTR(" ");
  3045.         }
  3046.         msg_prt_line(line);
  3047.         flushbuf();                        /* show one line at a time */
  3048.  
  3049.         /* Definition continues until line that doesn't end with '\' */
  3050.         if (got_int || type != FIND_DEFINE || p < line || *p != '\\')
  3051.             break;
  3052.         
  3053.         if (fp != NULL)
  3054.         {
  3055.             if (vim_fgets(line, LSIZE, fp))    /* end of file */
  3056.                 break;
  3057.             ++*lnum;
  3058.         }
  3059.         else
  3060.         {
  3061.             if (++*lnum > curbuf->b_ml.ml_line_count)
  3062.                 break;
  3063.             line = ml_get(*lnum);
  3064.         }
  3065.         msg_outchar('\n');
  3066.     }
  3067. }
  3068.  
  3069. #ifdef VIMINFO
  3070.     int
  3071. read_viminfo_search_pattern(line, fp, force)
  3072.     char_u    *line;
  3073.     FILE    *fp;
  3074.     int        force;
  3075. {
  3076.     char_u    *lp;
  3077.     char_u    **pattern;
  3078.  
  3079.     lp = line;
  3080.     if (lp[0] == '~')
  3081.         lp++;
  3082.     if (lp[0] == '/')
  3083.         pattern = &search_pattern;
  3084.     else
  3085.         pattern = &subst_pattern;
  3086.     if (*pattern != NULL && force)
  3087.         vim_free(*pattern);
  3088.     if (force || *pattern == NULL)
  3089.     {
  3090.         viminfo_readstring(lp);
  3091.         *pattern = strsave(lp + 1);
  3092.         if (line[0] == '~')
  3093.             last_pattern = *pattern;
  3094.     }
  3095.     return vim_fgets(line, LSIZE, fp);
  3096. }
  3097.  
  3098.     void
  3099. write_viminfo_search_pattern(fp)
  3100.     FILE    *fp;
  3101. {
  3102.     if (get_viminfo_parameter('/') != 0)
  3103.     {
  3104.         if (search_pattern != NULL)
  3105.         {
  3106.             fprintf(fp, "\n# Last Search Pattern:\n");
  3107.             fprintf(fp, "%s/", (last_pattern == search_pattern) ? "~" : "");
  3108.             viminfo_writestring(fp, search_pattern);
  3109.         }
  3110.         if (subst_pattern != NULL)
  3111.         {
  3112.             fprintf(fp, "\n# Last Substitute Search Pattern:\n");
  3113.             fprintf(fp, "%s&", (last_pattern == subst_pattern) ? "~" : "");
  3114.             viminfo_writestring(fp, subst_pattern);
  3115.         }
  3116.     }
  3117. }
  3118. #endif /* VIMINFO */
  3119.  
  3120. /*
  3121.  * Give a warning message.
  3122.  * Use 'w' highlighting and may repeat the message after redrawing
  3123.  */
  3124.     static void
  3125. give_warning(message)
  3126.     char_u    *message;
  3127. {
  3128.     (void)set_highlight('w');
  3129.     msg_highlight = TRUE;
  3130.     if (msg(message) && !msg_scroll)
  3131.     {
  3132.         keep_msg = message;
  3133.         keep_msg_highlight = 'w';
  3134.     }
  3135.     msg_didout = FALSE;        /* overwrite this message */
  3136.     msg_col = 0;
  3137. }
  3138.